mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-16 10:43:29 +00:00
cgo: support errno value as second return parameter
Making this work on all targets was interesting but there's now a test in place to make sure this works on all targets that have the CGo test enabled (which is almost all targets).
This commit is contained in:
@@ -34,6 +34,7 @@ var libPicolibc = Library{
|
|||||||
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
"-D__OBSOLETE_MATH_FLOAT=1", // use old math code that doesn't expect a FPU
|
||||||
"-D__OBSOLETE_MATH_DOUBLE=0",
|
"-D__OBSOLETE_MATH_DOUBLE=0",
|
||||||
"-D_WANT_IO_C99_FORMATS",
|
"-D_WANT_IO_C99_FORMATS",
|
||||||
|
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", newlibDir + "/libc/include",
|
"-isystem", newlibDir + "/libc/include",
|
||||||
"-I" + newlibDir + "/libc/tinystdio",
|
"-I" + newlibDir + "/libc/tinystdio",
|
||||||
|
|||||||
+117
-4
@@ -138,7 +138,8 @@ typedef unsigned long long _Cgo_ulonglong;
|
|||||||
// first.
|
// first.
|
||||||
// These functions will be modified to get a "C." prefix, so the source below
|
// These functions will be modified to get a "C." prefix, so the source below
|
||||||
// doesn't reflect the final AST.
|
// doesn't reflect the final AST.
|
||||||
const generatedGoFilePrefix = `
|
const generatedGoFilePrefixBase = `
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -169,6 +170,74 @@ func __CBytes([]byte) unsafe.Pointer
|
|||||||
func CBytes(b []byte) unsafe.Pointer {
|
func CBytes(b []byte) unsafe.Pointer {
|
||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func __get_errno_num() uintptr
|
||||||
|
`
|
||||||
|
|
||||||
|
const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
|
||||||
|
func __get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// Windows uses fake errno values in the syscall package.
|
||||||
|
// See for example: https://github.com/golang/go/issues/23468
|
||||||
|
// TinyGo uses mingw-w64 though, which does have defined errno values. Since the
|
||||||
|
// syscall package is the standard library one we can't change it, but we can
|
||||||
|
// map the errno values to match the values in the syscall package.
|
||||||
|
// Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
|
||||||
|
const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
|
||||||
|
var __errno_mapping = [...]syscall.Errno{
|
||||||
|
1: syscall.EPERM,
|
||||||
|
2: syscall.ENOENT,
|
||||||
|
3: syscall.ESRCH,
|
||||||
|
4: syscall.EINTR,
|
||||||
|
5: syscall.EIO,
|
||||||
|
6: syscall.ENXIO,
|
||||||
|
7: syscall.E2BIG,
|
||||||
|
8: syscall.ENOEXEC,
|
||||||
|
9: syscall.EBADF,
|
||||||
|
10: syscall.ECHILD,
|
||||||
|
11: syscall.EAGAIN,
|
||||||
|
12: syscall.ENOMEM,
|
||||||
|
13: syscall.EACCES,
|
||||||
|
14: syscall.EFAULT,
|
||||||
|
16: syscall.EBUSY,
|
||||||
|
17: syscall.EEXIST,
|
||||||
|
18: syscall.EXDEV,
|
||||||
|
19: syscall.ENODEV,
|
||||||
|
20: syscall.ENOTDIR,
|
||||||
|
21: syscall.EISDIR,
|
||||||
|
22: syscall.EINVAL,
|
||||||
|
23: syscall.ENFILE,
|
||||||
|
24: syscall.EMFILE,
|
||||||
|
25: syscall.ENOTTY,
|
||||||
|
27: syscall.EFBIG,
|
||||||
|
28: syscall.ENOSPC,
|
||||||
|
29: syscall.ESPIPE,
|
||||||
|
30: syscall.EROFS,
|
||||||
|
31: syscall.EMLINK,
|
||||||
|
32: syscall.EPIPE,
|
||||||
|
33: syscall.EDOM,
|
||||||
|
34: syscall.ERANGE,
|
||||||
|
36: syscall.EDEADLK,
|
||||||
|
38: syscall.ENAMETOOLONG,
|
||||||
|
39: syscall.ENOLCK,
|
||||||
|
40: syscall.ENOSYS,
|
||||||
|
41: syscall.ENOTEMPTY,
|
||||||
|
42: syscall.EILSEQ,
|
||||||
|
}
|
||||||
|
|
||||||
|
func __get_errno() error {
|
||||||
|
num := C.__get_errno_num()
|
||||||
|
if num < uintptr(len(__errno_mapping)) {
|
||||||
|
if mapped := __errno_mapping[num]; mapped != 0 {
|
||||||
|
return mapped
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return syscall.Errno(num)
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
// Process extracts `import "C"` statements from the AST, parses the comment
|
// Process extracts `import "C"` statements from the AST, parses the comment
|
||||||
@@ -178,7 +247,7 @@ func CBytes(b []byte) unsafe.Pointer {
|
|||||||
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
|
||||||
// hashes of the accessed C header files. If there is one or more error, it
|
// hashes of the accessed C header files. If there is one or more error, it
|
||||||
// returns these in the []error slice but still modifies the AST.
|
// returns these in the []error slice but still modifies the AST.
|
||||||
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cflags []string, goos string) ([]*ast.File, []string, []string, []string, map[string][]byte, []error) {
|
||||||
p := &cgoPackage{
|
p := &cgoPackage{
|
||||||
packageName: files[0].Name.Name,
|
packageName: files[0].Name.Name,
|
||||||
currentDir: dir,
|
currentDir: dir,
|
||||||
@@ -210,7 +279,12 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
// Construct a new in-memory AST for CGo declarations of this package.
|
// Construct a new in-memory AST for CGo declarations of this package.
|
||||||
// The first part is written as Go code that is then parsed, but more code
|
// The first part is written as Go code that is then parsed, but more code
|
||||||
// is added later to the AST to declare functions, globals, etc.
|
// is added later to the AST to declare functions, globals, etc.
|
||||||
goCode := "package " + files[0].Name.Name + "\n\n" + generatedGoFilePrefix
|
goCode := "package " + files[0].Name.Name + "\n\n"
|
||||||
|
if goos == "windows" {
|
||||||
|
goCode += generatedGoFilePrefixWindows
|
||||||
|
} else {
|
||||||
|
goCode += generatedGoFilePrefixOther
|
||||||
|
}
|
||||||
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
|
p.generated, err = parser.ParseFile(fset, dir+"/!cgo.go", goCode, parser.ParseComments)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// This is always a bug in the cgo package.
|
// This is always a bug in the cgo package.
|
||||||
@@ -225,7 +299,7 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
|
|||||||
switch decl := decl.(type) {
|
switch decl := decl.(type) {
|
||||||
case *ast.FuncDecl:
|
case *ast.FuncDecl:
|
||||||
switch decl.Name.Name {
|
switch decl.Name.Name {
|
||||||
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes", "CBytes", "__CBytes":
|
case "CString", "GoString", "GoStringN", "__GoStringN", "GoBytes", "__GoBytes", "CBytes", "__CBytes", "__get_errno_num", "__get_errno", "__errno_mapping":
|
||||||
// Adjust the name to have a "C." prefix so it is correctly
|
// Adjust the name to have a "C." prefix so it is correctly
|
||||||
// resolved.
|
// resolved.
|
||||||
decl.Name.Name = "C." + decl.Name.Name
|
decl.Name.Name = "C." + decl.Name.Name
|
||||||
@@ -1279,6 +1353,45 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
|
|||||||
// separate namespace (no _Cgo_ hacks like in gc).
|
// separate namespace (no _Cgo_ hacks like in gc).
|
||||||
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
|
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
|
||||||
switch node := cursor.Node().(type) {
|
switch node := cursor.Node().(type) {
|
||||||
|
case *ast.AssignStmt:
|
||||||
|
// An assign statement could be something like this:
|
||||||
|
//
|
||||||
|
// val, errno := C.some_func()
|
||||||
|
//
|
||||||
|
// Check whether it looks like that, and if so, read the errno value and
|
||||||
|
// return it as the second return value. The call will be transformed
|
||||||
|
// into something like this:
|
||||||
|
//
|
||||||
|
// val, errno := C.some_func(), C.__get_errno()
|
||||||
|
if len(node.Lhs) != 2 || len(node.Rhs) != 1 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
rhs, ok := node.Rhs[0].(*ast.CallExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
fun, ok := rhs.Fun.(*ast.SelectorExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
x, ok := fun.X.(*ast.Ident)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if found, ok := names[fun.Sel.Name]; ok && x.Name == "C" {
|
||||||
|
// Replace "C"."some_func" into "C.somefunc".
|
||||||
|
rhs.Fun = &ast.Ident{
|
||||||
|
NamePos: x.NamePos,
|
||||||
|
Name: f.getASTDeclName(fun.Sel.Name, found, true),
|
||||||
|
}
|
||||||
|
// Add the errno value as the second value in the statement.
|
||||||
|
node.Rhs = append(node.Rhs, &ast.CallExpr{
|
||||||
|
Fun: &ast.Ident{
|
||||||
|
NamePos: node.Lhs[1].End(),
|
||||||
|
Name: "C.__get_errno",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
case *ast.CallExpr:
|
case *ast.CallExpr:
|
||||||
fun, ok := node.Fun.(*ast.SelectorExpr)
|
fun, ok := node.Fun.(*ast.SelectorExpr)
|
||||||
if !ok {
|
if !ok {
|
||||||
|
|||||||
+23
-4
@@ -56,7 +56,7 @@ func TestCGo(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the AST with CGo.
|
// Process the AST with CGo.
|
||||||
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags)
|
cgoFiles, _, _, _, _, cgoErrors := Process([]*ast.File{f}, "testdata", "main", fset, cflags, "linux")
|
||||||
|
|
||||||
// Check the AST for type errors.
|
// Check the AST for type errors.
|
||||||
var typecheckErrors []error
|
var typecheckErrors []error
|
||||||
@@ -64,7 +64,7 @@ func TestCGo(t *testing.T) {
|
|||||||
Error: func(err error) {
|
Error: func(err error) {
|
||||||
typecheckErrors = append(typecheckErrors, err)
|
typecheckErrors = append(typecheckErrors, err)
|
||||||
},
|
},
|
||||||
Importer: simpleImporter{},
|
Importer: newSimpleImporter(),
|
||||||
Sizes: types.SizesFor("gccgo", "arm"),
|
Sizes: types.SizesFor("gccgo", "arm"),
|
||||||
}
|
}
|
||||||
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
_, err = config.Check("", fset, append([]*ast.File{f}, cgoFiles...), nil)
|
||||||
@@ -202,14 +202,33 @@ func Test_cgoPackage_isEquivalentAST(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// simpleImporter implements the types.Importer interface, but only allows
|
// simpleImporter implements the types.Importer interface, but only allows
|
||||||
// importing the unsafe package.
|
// importing the syscall and unsafe packages.
|
||||||
type simpleImporter struct {
|
type simpleImporter struct {
|
||||||
|
syscallPkg *types.Package
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSimpleImporter() *simpleImporter {
|
||||||
|
i := &simpleImporter{}
|
||||||
|
|
||||||
|
// Implement a dummy syscall package with the Errno type.
|
||||||
|
i.syscallPkg = types.NewPackage("syscall", "syscall")
|
||||||
|
obj := types.NewTypeName(token.NoPos, i.syscallPkg, "Errno", nil)
|
||||||
|
named := types.NewNamed(obj, nil, nil)
|
||||||
|
i.syscallPkg.Scope().Insert(obj)
|
||||||
|
named.SetUnderlying(types.Typ[types.Uintptr])
|
||||||
|
sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(), types.NewTuple(types.NewParam(token.NoPos, i.syscallPkg, "", types.Typ[types.String])), false)
|
||||||
|
named.AddMethod(types.NewFunc(token.NoPos, i.syscallPkg, "Error", sig))
|
||||||
|
i.syscallPkg.MarkComplete()
|
||||||
|
|
||||||
|
return i
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import implements the Importer interface. For testing usage only: it only
|
// Import implements the Importer interface. For testing usage only: it only
|
||||||
// supports importing the unsafe package.
|
// supports importing the unsafe package.
|
||||||
func (i simpleImporter) Import(path string) (*types.Package, error) {
|
func (i *simpleImporter) Import(path string) (*types.Package, error) {
|
||||||
switch path {
|
switch path {
|
||||||
|
case "syscall":
|
||||||
|
return i.syscallPkg, nil
|
||||||
case "unsafe":
|
case "unsafe":
|
||||||
return types.Unsafe, nil
|
return types.Unsafe, nil
|
||||||
default:
|
default:
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -55,6 +56,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -36,6 +37,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
Vendored
+8
@@ -1,5 +1,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
import "unsafe"
|
import "unsafe"
|
||||||
|
|
||||||
var _ unsafe.Pointer
|
var _ unsafe.Pointer
|
||||||
@@ -31,6 +32,13 @@ func C.CBytes(b []byte) unsafe.Pointer {
|
|||||||
return C.__CBytes(b)
|
return C.__CBytes(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname C.__get_errno_num runtime.cgo_errno
|
||||||
|
func C.__get_errno_num() uintptr
|
||||||
|
|
||||||
|
func C.__get_errno() error {
|
||||||
|
return syscall.Errno(C.__get_errno_num())
|
||||||
|
}
|
||||||
|
|
||||||
type (
|
type (
|
||||||
C.char uint8
|
C.char uint8
|
||||||
C.schar int8
|
C.schar int8
|
||||||
|
|||||||
@@ -331,6 +331,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
|||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "include"),
|
"-isystem", filepath.Join(picolibcDir, "include"),
|
||||||
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
"-isystem", filepath.Join(picolibcDir, "tinystdio"),
|
||||||
|
"-D__PICOLIBC_ERRNO_FUNCTION=__errno_location",
|
||||||
)
|
)
|
||||||
case "musl":
|
case "musl":
|
||||||
root := goenv.Get("TINYGOROOT")
|
root := goenv.Get("TINYGOROOT")
|
||||||
@@ -340,6 +341,7 @@ func (c *Config) CFlags(libclang bool) []string {
|
|||||||
"-nostdlibinc",
|
"-nostdlibinc",
|
||||||
"-isystem", filepath.Join(path, "include"),
|
"-isystem", filepath.Join(path, "include"),
|
||||||
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
"-isystem", filepath.Join(root, "lib", "musl", "arch", arch),
|
||||||
|
"-isystem", filepath.Join(root, "lib", "musl", "arch", "generic"),
|
||||||
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
"-isystem", filepath.Join(root, "lib", "musl", "include"),
|
||||||
)
|
)
|
||||||
case "wasi-libc":
|
case "wasi-libc":
|
||||||
|
|||||||
+1
-1
@@ -485,7 +485,7 @@ func (p *Package) parseFiles() ([]*ast.File, error) {
|
|||||||
var initialCFlags []string
|
var initialCFlags []string
|
||||||
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
initialCFlags = append(initialCFlags, p.program.config.CFlags(true)...)
|
||||||
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
initialCFlags = append(initialCFlags, "-I"+p.Dir)
|
||||||
generated, headerCode, cflags, ldflags, accessedFiles, errs := cgo.Process(files, p.program.workingDir, p.ImportPath, p.program.fset, initialCFlags)
|
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.CFlags = append(initialCFlags, cflags...)
|
||||||
p.CGoHeaders = headerCode
|
p.CGoHeaders = headerCode
|
||||||
for path, hash := range accessedFiles {
|
for path, hash := range accessedFiles {
|
||||||
|
|||||||
@@ -86,3 +86,16 @@ func AdjustTimeOffset(offset int64) {
|
|||||||
// TODO: do this atomically?
|
// TODO: do this atomically?
|
||||||
timeOffset += offset
|
timeOffset += offset
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Picolibc is not configured to define its own errno value, instead it calls
|
||||||
|
// __errno_location.
|
||||||
|
// TODO: a global works well enough for now (same as errno on Linux with
|
||||||
|
// -scheduler=tasks), but this should ideally be a thread-local variable stored
|
||||||
|
// in task.Task.
|
||||||
|
// Especially when we add multicore support for microcontrollers.
|
||||||
|
var errno int32
|
||||||
|
|
||||||
|
//export __errno_location
|
||||||
|
func libc_errno_location() *int32 {
|
||||||
|
return &errno
|
||||||
|
}
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ func syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
|||||||
r1 = uintptr(result)
|
r1 = uintptr(result)
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
// Syscall returns -1 on failure.
|
// Syscall returns -1 on failure.
|
||||||
err = uintptr(*libc___error())
|
err = uintptr(*libc_errno_location())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -161,7 +161,7 @@ func syscall_syscallX(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
|||||||
r1 = call_syscallX(fn, a1, a2, a3)
|
r1 = call_syscallX(fn, a1, a2, a3)
|
||||||
if int64(r1) == -1 {
|
if int64(r1) == -1 {
|
||||||
// Syscall returns -1 on failure.
|
// Syscall returns -1 on failure.
|
||||||
err = uintptr(*libc___error())
|
err = uintptr(*libc_errno_location())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -171,7 +171,7 @@ func syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) {
|
|||||||
r1 = call_syscallX(fn, a1, a2, a3)
|
r1 = call_syscallX(fn, a1, a2, a3)
|
||||||
if r1 == 0 {
|
if r1 == 0 {
|
||||||
// Syscall returns a pointer on success, or NULL on failure.
|
// Syscall returns a pointer on success, or NULL on failure.
|
||||||
err = uintptr(*libc___error())
|
err = uintptr(*libc_errno_location())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -182,7 +182,7 @@ func syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
|||||||
r1 = uintptr(result)
|
r1 = uintptr(result)
|
||||||
if result == -1 {
|
if result == -1 {
|
||||||
// Syscall returns -1 on failure.
|
// Syscall returns -1 on failure.
|
||||||
err = uintptr(*libc___error())
|
err = uintptr(*libc_errno_location())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -192,7 +192,7 @@ func syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
|||||||
r1 = call_syscall6X(fn, a1, a2, a3, a4, a5, a6)
|
r1 = call_syscall6X(fn, a1, a2, a3, a4, a5, a6)
|
||||||
if int64(r1) == -1 {
|
if int64(r1) == -1 {
|
||||||
// Syscall returns -1 on failure.
|
// Syscall returns -1 on failure.
|
||||||
err = uintptr(*libc___error())
|
err = uintptr(*libc_errno_location())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ func libc_getpagesize() int32
|
|||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
//export __error
|
//export __error
|
||||||
func libc___error() *int32
|
func libc_errno_location() *int32
|
||||||
|
|
||||||
//export tinygo_syscall
|
//export tinygo_syscall
|
||||||
func call_syscall(fn, a1, a2, a3 uintptr) int32
|
func call_syscall(fn, a1, a2, a3 uintptr) int32
|
||||||
|
|||||||
@@ -113,3 +113,6 @@ func syscall_Getpagesize() int {
|
|||||||
_GetSystemInfo(unsafe.Pointer(&info))
|
_GetSystemInfo(unsafe.Pointer(&info))
|
||||||
return int(info.dwpagesize)
|
return int(info.dwpagesize)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//export _errno
|
||||||
|
func libc_errno_location() *int32
|
||||||
|
|||||||
@@ -127,3 +127,8 @@ func write(fd uintptr, p unsafe.Pointer, n int32) int32 {
|
|||||||
func getAuxv() []uintptr {
|
func getAuxv() []uintptr {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called from cgo to obtain the errno value.
|
||||||
|
func cgo_errno() uintptr {
|
||||||
|
return uintptr(*libc_errno_location())
|
||||||
|
}
|
||||||
|
|||||||
@@ -313,3 +313,9 @@ func hardwareRand() (n uint64, ok bool) {
|
|||||||
// TODO: see whether there is a RNG and use it.
|
// TODO: see whether there is a RNG and use it.
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func libc_errno_location() *int32 {
|
||||||
|
// CGo is unavailable, so this function should be unreachable.
|
||||||
|
runtimePanic("runtime: no cgo errno")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -112,3 +112,8 @@ func hardwareRand() (n uint64, ok bool) {
|
|||||||
//
|
//
|
||||||
//export arc4random
|
//export arc4random
|
||||||
func libc_arc4random() uint32
|
func libc_arc4random() uint32
|
||||||
|
|
||||||
|
// int *__errno_location(void);
|
||||||
|
//
|
||||||
|
//export __errno_location
|
||||||
|
func libc_errno_location() *int32
|
||||||
|
|||||||
@@ -51,3 +51,9 @@ func procUnpin() {
|
|||||||
func hardwareRand() (n uint64, ok bool) {
|
func hardwareRand() (n uint64, ok bool) {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func libc_errno_location() *int32 {
|
||||||
|
// CGo is unavailable, so this function should be unreachable.
|
||||||
|
runtimePanic("runtime: no cgo errno")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -81,3 +81,9 @@ func procUnpin() {
|
|||||||
func hardwareRand() (n uint64, ok bool) {
|
func hardwareRand() (n uint64, ok bool) {
|
||||||
return random.GetRandomU64(), true
|
return random.GetRandomU64(), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func libc_errno_location() *int32 {
|
||||||
|
// CGo is unavailable, so this function should be unreachable.
|
||||||
|
runtimePanic("runtime: no cgo errno")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+5
@@ -77,3 +77,8 @@ double doSqrt(double x) {
|
|||||||
void printf_single_int(char *format, int arg) {
|
void printf_single_int(char *format, int arg) {
|
||||||
printf(format, arg);
|
printf(format, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int set_errno(int err) {
|
||||||
|
errno = err;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|||||||
Vendored
+11
-1
@@ -18,7 +18,10 @@ import "C"
|
|||||||
// static int headerfunc_static(int a) { return a - 1; }
|
// static int headerfunc_static(int a) { return a - 1; }
|
||||||
import "C"
|
import "C"
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
println("fortytwo:", C.fortytwo())
|
println("fortytwo:", C.fortytwo())
|
||||||
@@ -168,6 +171,13 @@ func main() {
|
|||||||
println("len(C.GoBytes(C.CBytes(nil),0)):", len(C.GoBytes(C.CBytes(nil), 0)))
|
println("len(C.GoBytes(C.CBytes(nil),0)):", len(C.GoBytes(C.CBytes(nil), 0)))
|
||||||
println(`rountrip CBytes:`, C.GoString((*C.char)(C.CBytes([]byte("hello\000")))))
|
println(`rountrip CBytes:`, C.GoString((*C.char)(C.CBytes([]byte("hello\000")))))
|
||||||
|
|
||||||
|
// Check that errno is returned from the second return value, and that it
|
||||||
|
// matches the errno value that was just set.
|
||||||
|
_, errno := C.set_errno(C.EINVAL)
|
||||||
|
println("EINVAL:", errno == syscall.EINVAL)
|
||||||
|
_, errno = C.set_errno(C.EAGAIN)
|
||||||
|
println("EAGAIN:", errno == syscall.EAGAIN)
|
||||||
|
|
||||||
// libc: test whether C functions work at all.
|
// libc: test whether C functions work at all.
|
||||||
buf1 := []byte("foobar\x00")
|
buf1 := []byte("foobar\x00")
|
||||||
buf2 := make([]byte, len(buf1))
|
buf2 := make([]byte, len(buf1))
|
||||||
|
|||||||
Vendored
+3
@@ -1,5 +1,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
typedef short myint;
|
typedef short myint;
|
||||||
typedef short unusedTypedef;
|
typedef short unusedTypedef;
|
||||||
@@ -154,3 +155,5 @@ void arraydecay(int buf1[5], int buf2[3][8], arraydecay_buf3 buf3);
|
|||||||
double doSqrt(double);
|
double doSqrt(double);
|
||||||
|
|
||||||
void printf_single_int(char *format, int arg);
|
void printf_single_int(char *format, int arg);
|
||||||
|
|
||||||
|
int set_errno(int err);
|
||||||
|
|||||||
Vendored
+2
@@ -75,6 +75,8 @@ len(C.GoStringN(nil, 0)): 0
|
|||||||
len(C.GoBytes(nil, 0)): 0
|
len(C.GoBytes(nil, 0)): 0
|
||||||
len(C.GoBytes(C.CBytes(nil),0)): 0
|
len(C.GoBytes(C.CBytes(nil),0)): 0
|
||||||
rountrip CBytes: hello
|
rountrip CBytes: hello
|
||||||
|
EINVAL: true
|
||||||
|
EAGAIN: true
|
||||||
copied string: foobar
|
copied string: foobar
|
||||||
CGo sqrt(3): +1.732051e+000
|
CGo sqrt(3): +1.732051e+000
|
||||||
C sqrt(3): +1.732051e+000
|
C sqrt(3): +1.732051e+000
|
||||||
|
|||||||
Reference in New Issue
Block a user