@@ -138,7 +138,8 @@ typedef unsigned long long _Cgo_ulonglong;
138138// first.
139139// These functions will be modified to get a "C." prefix, so the source below
140140// doesn't reflect the final AST.
141- const generatedGoFilePrefix = `
141+ const generatedGoFilePrefixBase = `
142+ import "syscall"
142143import "unsafe"
143144
144145var _ unsafe.Pointer
@@ -169,6 +170,74 @@ func __CBytes([]byte) unsafe.Pointer
169170func CBytes(b []byte) unsafe.Pointer {
170171 return C.__CBytes(b)
171172}
173+
174+ //go:linkname C.__get_errno_num runtime.cgo_errno
175+ func __get_errno_num() uintptr
176+ `
177+
178+ const generatedGoFilePrefixOther = generatedGoFilePrefixBase + `
179+ func __get_errno() error {
180+ return syscall.Errno(C.__get_errno_num())
181+ }
182+ `
183+
184+ // Windows uses fake errno values in the syscall package.
185+ // See for example: https://github.com/golang/go/issues/23468
186+ // TinyGo uses mingw-w64 though, which does have defined errno values. Since the
187+ // syscall package is the standard library one we can't change it, but we can
188+ // map the errno values to match the values in the syscall package.
189+ // Source of the errno values: lib/mingw-w64/mingw-w64-headers/crt/errno.h
190+ const generatedGoFilePrefixWindows = generatedGoFilePrefixBase + `
191+ var __errno_mapping = [...]syscall.Errno{
192+ 1: syscall.EPERM,
193+ 2: syscall.ENOENT,
194+ 3: syscall.ESRCH,
195+ 4: syscall.EINTR,
196+ 5: syscall.EIO,
197+ 6: syscall.ENXIO,
198+ 7: syscall.E2BIG,
199+ 8: syscall.ENOEXEC,
200+ 9: syscall.EBADF,
201+ 10: syscall.ECHILD,
202+ 11: syscall.EAGAIN,
203+ 12: syscall.ENOMEM,
204+ 13: syscall.EACCES,
205+ 14: syscall.EFAULT,
206+ 16: syscall.EBUSY,
207+ 17: syscall.EEXIST,
208+ 18: syscall.EXDEV,
209+ 19: syscall.ENODEV,
210+ 20: syscall.ENOTDIR,
211+ 21: syscall.EISDIR,
212+ 22: syscall.EINVAL,
213+ 23: syscall.ENFILE,
214+ 24: syscall.EMFILE,
215+ 25: syscall.ENOTTY,
216+ 27: syscall.EFBIG,
217+ 28: syscall.ENOSPC,
218+ 29: syscall.ESPIPE,
219+ 30: syscall.EROFS,
220+ 31: syscall.EMLINK,
221+ 32: syscall.EPIPE,
222+ 33: syscall.EDOM,
223+ 34: syscall.ERANGE,
224+ 36: syscall.EDEADLK,
225+ 38: syscall.ENAMETOOLONG,
226+ 39: syscall.ENOLCK,
227+ 40: syscall.ENOSYS,
228+ 41: syscall.ENOTEMPTY,
229+ 42: syscall.EILSEQ,
230+ }
231+
232+ func __get_errno() error {
233+ num := C.__get_errno_num()
234+ if num < uintptr(len(__errno_mapping)) {
235+ if mapped := __errno_mapping[num]; mapped != 0 {
236+ return mapped
237+ }
238+ }
239+ return syscall.Errno(num)
240+ }
172241`
173242
174243// Process extracts `import "C"` statements from the AST, parses the comment
@@ -178,7 +247,7 @@ func CBytes(b []byte) unsafe.Pointer {
178247// functions), the CFLAGS and LDFLAGS found in #cgo lines, and a map of file
179248// hashes of the accessed C header files. If there is one or more error, it
180249// returns these in the []error slice but still modifies the AST.
181- func Process (files []* ast.File , dir , importPath string , fset * token.FileSet , cflags []string ) ([]* ast.File , []string , []string , []string , map [string ][]byte , []error ) {
250+ func Process (files []* ast.File , dir , importPath string , fset * token.FileSet , cflags []string , goos string ) ([]* ast.File , []string , []string , []string , map [string ][]byte , []error ) {
182251 p := & cgoPackage {
183252 packageName : files [0 ].Name .Name ,
184253 currentDir : dir ,
@@ -210,7 +279,12 @@ func Process(files []*ast.File, dir, importPath string, fset *token.FileSet, cfl
210279 // Construct a new in-memory AST for CGo declarations of this package.
211280 // The first part is written as Go code that is then parsed, but more code
212281 // is added later to the AST to declare functions, globals, etc.
213- goCode := "package " + files [0 ].Name .Name + "\n \n " + generatedGoFilePrefix
282+ goCode := "package " + files [0 ].Name .Name + "\n \n "
283+ if goos == "windows" {
284+ goCode += generatedGoFilePrefixWindows
285+ } else {
286+ goCode += generatedGoFilePrefixOther
287+ }
214288 p .generated , err = parser .ParseFile (fset , dir + "/!cgo.go" , goCode , parser .ParseComments )
215289 if err != nil {
216290 // 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
225299 switch decl := decl .(type ) {
226300 case * ast.FuncDecl :
227301 switch decl .Name .Name {
228- case "CString" , "GoString" , "GoStringN" , "__GoStringN" , "GoBytes" , "__GoBytes" , "CBytes" , "__CBytes" :
302+ case "CString" , "GoString" , "GoStringN" , "__GoStringN" , "GoBytes" , "__GoBytes" , "CBytes" , "__CBytes" , "__get_errno_num" , "__get_errno" , "__errno_mapping" :
229303 // Adjust the name to have a "C." prefix so it is correctly
230304 // resolved.
231305 decl .Name .Name = "C." + decl .Name .Name
@@ -1279,6 +1353,45 @@ extern __typeof(%s) %s __attribute__((alias(%#v)));
12791353// separate namespace (no _Cgo_ hacks like in gc).
12801354func (f * cgoFile ) walker (cursor * astutil.Cursor , names map [string ]clangCursor ) bool {
12811355 switch node := cursor .Node ().(type ) {
1356+ case * ast.AssignStmt :
1357+ // An assign statement could be something like this:
1358+ //
1359+ // val, errno := C.some_func()
1360+ //
1361+ // Check whether it looks like that, and if so, read the errno value and
1362+ // return it as the second return value. The call will be transformed
1363+ // into something like this:
1364+ //
1365+ // val, errno := C.some_func(), C.__get_errno()
1366+ if len (node .Lhs ) != 2 || len (node .Rhs ) != 1 {
1367+ return true
1368+ }
1369+ rhs , ok := node .Rhs [0 ].(* ast.CallExpr )
1370+ if ! ok {
1371+ return true
1372+ }
1373+ fun , ok := rhs .Fun .(* ast.SelectorExpr )
1374+ if ! ok {
1375+ return true
1376+ }
1377+ x , ok := fun .X .(* ast.Ident )
1378+ if ! ok {
1379+ return true
1380+ }
1381+ if found , ok := names [fun .Sel .Name ]; ok && x .Name == "C" {
1382+ // Replace "C"."some_func" into "C.somefunc".
1383+ rhs .Fun = & ast.Ident {
1384+ NamePos : x .NamePos ,
1385+ Name : f .getASTDeclName (fun .Sel .Name , found , true ),
1386+ }
1387+ // Add the errno value as the second value in the statement.
1388+ node .Rhs = append (node .Rhs , & ast.CallExpr {
1389+ Fun : & ast.Ident {
1390+ NamePos : node .Lhs [1 ].End (),
1391+ Name : "C.__get_errno" ,
1392+ },
1393+ })
1394+ }
12821395 case * ast.CallExpr :
12831396 fun , ok := node .Fun .(* ast.SelectorExpr )
12841397 if ! ok {
0 commit comments