aboutsummaryrefslogtreecommitdiffhomepage
path: root/cgo/cgo.go
diff options
context:
space:
mode:
Diffstat (limited to 'cgo/cgo.go')
-rw-r--r--cgo/cgo.go121
1 files changed, 117 insertions, 4 deletions
diff --git a/cgo/cgo.go b/cgo/cgo.go
index a90b30753..0ed26e3fb 100644
--- a/cgo/cgo.go
+++ b/cgo/cgo.go
@@ -138,7 +138,8 @@ typedef unsigned long long _Cgo_ulonglong;
// first.
// These functions will be modified to get a "C." prefix, so the source below
// doesn't reflect the final AST.
-const generatedGoFilePrefix = `
+const generatedGoFilePrefixBase = `
+import "syscall"
import "unsafe"
var _ unsafe.Pointer
@@ -169,6 +170,74 @@ func __CBytes([]byte) unsafe.Pointer
func CBytes(b []byte) unsafe.Pointer {
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
@@ -178,7 +247,7 @@ func CBytes(b []byte) unsafe.Pointer {
// 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
// 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{
packageName: files[0].Name.Name,
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.
// 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.
- 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)
if err != nil {
// 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) {
case *ast.FuncDecl:
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
// resolved.
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).
func (f *cgoFile) walker(cursor *astutil.Cursor, names map[string]clangCursor) bool {
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:
fun, ok := node.Fun.(*ast.SelectorExpr)
if !ok {