github.com/orangeji11/golang-sys-sw64@v0.0.0-20221228054527-b72799809e00/unix/mksyscall.go (about)

     1  // Copyright 2018 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build ignore
     6  
     7  /*
     8  This program reads a file containing function prototypes
     9  (like syscall_darwin.go) and generates system call bodies.
    10  The prototypes are marked by lines beginning with "//sys"
    11  and read like func declarations if //sys is replaced by func, but:
    12  	* The parameter lists must give a name for each argument.
    13  	  This includes return parameters.
    14  	* The parameter lists must give a type for each argument:
    15  	  the (x, y, z int) shorthand is not allowed.
    16  	* If the return parameter is an error number, it must be named errno.
    17  
    18  A line beginning with //sysnb is like //sys, except that the
    19  goroutine will not be suspended during the execution of the system
    20  call.  This must only be used for system calls which can never
    21  block, as otherwise the system call could cause all goroutines to
    22  hang.
    23  */
    24  package main
    25  
    26  import (
    27  	"bufio"
    28  	"flag"
    29  	"fmt"
    30  	"os"
    31  	"regexp"
    32  	"strings"
    33  )
    34  
    35  var (
    36  	b32       = flag.Bool("b32", false, "32bit big-endian")
    37  	l32       = flag.Bool("l32", false, "32bit little-endian")
    38  	plan9     = flag.Bool("plan9", false, "plan9")
    39  	openbsd   = flag.Bool("openbsd", false, "openbsd")
    40  	netbsd    = flag.Bool("netbsd", false, "netbsd")
    41  	dragonfly = flag.Bool("dragonfly", false, "dragonfly")
    42  	arm       = flag.Bool("arm", false, "arm") // 64-bit value should use (even, odd)-pair
    43  	tags      = flag.String("tags", "", "build tags")
    44  	filename  = flag.String("output", "", "output file name (standard output if omitted)")
    45  )
    46  
    47  // cmdLine returns this programs's commandline arguments
    48  func cmdLine() string {
    49  	return "go run mksyscall.go " + strings.Join(os.Args[1:], " ")
    50  }
    51  
    52  // buildTags returns build tags
    53  func buildTags() string {
    54  	return *tags
    55  }
    56  
    57  // Param is function parameter
    58  type Param struct {
    59  	Name string
    60  	Type string
    61  }
    62  
    63  // usage prints the program usage
    64  func usage() {
    65  	fmt.Fprintf(os.Stderr, "usage: go run mksyscall.go [-b32 | -l32] [-tags x,y] [file ...]\n")
    66  	os.Exit(1)
    67  }
    68  
    69  // parseParamList parses parameter list and returns a slice of parameters
    70  func parseParamList(list string) []string {
    71  	list = strings.TrimSpace(list)
    72  	if list == "" {
    73  		return []string{}
    74  	}
    75  	return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
    76  }
    77  
    78  // parseParam splits a parameter into name and type
    79  func parseParam(p string) Param {
    80  	ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
    81  	if ps == nil {
    82  		fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
    83  		os.Exit(1)
    84  	}
    85  	return Param{ps[1], ps[2]}
    86  }
    87  
    88  func main() {
    89  	// Get the OS and architecture (using GOARCH_TARGET if it exists)
    90  	goos := os.Getenv("GOOS")
    91  	goarch := os.Getenv("GOARCH_TARGET")
    92  	if goarch == "" {
    93  		goarch = os.Getenv("GOARCH")
    94  	}
    95  
    96  	// Check that we are using the Docker-based build system if we should
    97  	if goos == "linux" {
    98  		if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
    99  			fmt.Fprintf(os.Stderr, "In the Docker-based build system, mksyscall should not be called directly.\n")
   100  			fmt.Fprintf(os.Stderr, "See README.md\n")
   101  			os.Exit(1)
   102  		}
   103  	}
   104  
   105  	flag.Usage = usage
   106  	flag.Parse()
   107  	if len(flag.Args()) <= 0 {
   108  		fmt.Fprintf(os.Stderr, "no files to parse provided\n")
   109  		usage()
   110  	}
   111  
   112  	endianness := ""
   113  	if *b32 {
   114  		endianness = "big-endian"
   115  	} else if *l32 {
   116  		endianness = "little-endian"
   117  	}
   118  
   119  	libc := false
   120  	if goos == "darwin" && strings.Contains(buildTags(), ",go1.12") {
   121  		libc = true
   122  	}
   123  	trampolines := map[string]bool{}
   124  
   125  	text := ""
   126  	for _, path := range flag.Args() {
   127  		file, err := os.Open(path)
   128  		if err != nil {
   129  			fmt.Fprintf(os.Stderr, err.Error())
   130  			os.Exit(1)
   131  		}
   132  		s := bufio.NewScanner(file)
   133  		for s.Scan() {
   134  			t := s.Text()
   135  			t = strings.TrimSpace(t)
   136  			t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
   137  			nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
   138  			if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
   139  				continue
   140  			}
   141  
   142  			// Line must be of the form
   143  			//	func Open(path string, mode int, perm int) (fd int, errno error)
   144  			// Split into name, in params, out params.
   145  			f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t)
   146  			if f == nil {
   147  				fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
   148  				os.Exit(1)
   149  			}
   150  			funct, inps, outps, sysname := f[2], f[3], f[4], f[5]
   151  
   152  			if funct == "Getpid" || funct == "Getppid" {
   153  				continue
   154  			}
   155  			// Split argument lists on comma.
   156  			in := parseParamList(inps)
   157  			out := parseParamList(outps)
   158  
   159  			// Try in vain to keep people from editing this file.
   160  			// The theory is that they jump into the middle of the file
   161  			// without reading the header.
   162  			text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
   163  
   164  			// Go function header.
   165  			outDecl := ""
   166  			if len(out) > 0 {
   167  				outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", "))
   168  			}
   169  			text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   170  
   171  			// Check if err return available
   172  			errvar := ""
   173  			for _, param := range out {
   174  				p := parseParam(param)
   175  				if p.Type == "error" {
   176  					errvar = p.Name
   177  					break
   178  				}
   179  			}
   180  
   181  			// Prepare arguments to Syscall.
   182  			var args []string
   183  			n := 0
   184  			for _, param := range in {
   185  				p := parseParam(param)
   186  				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   187  					args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
   188  				} else if p.Type == "string" && errvar != "" {
   189  					text += fmt.Sprintf("\tvar _p%d *byte\n", n)
   190  					text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name)
   191  					text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
   192  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   193  					n++
   194  				} else if p.Type == "string" {
   195  					fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
   196  					text += fmt.Sprintf("\tvar _p%d *byte\n", n)
   197  					text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name)
   198  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   199  					n++
   200  				} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
   201  					// Convert slice into pointer, length.
   202  					// Have to be careful not to take address of &a[0] if len == 0:
   203  					// pass dummy pointer in that case.
   204  					// Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
   205  					text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n)
   206  					text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name)
   207  					text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n)
   208  					args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
   209  					n++
   210  				} else if p.Type == "int64" && (*openbsd || *netbsd) {
   211  					args = append(args, "0")
   212  					if endianness == "big-endian" {
   213  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   214  					} else if endianness == "little-endian" {
   215  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   216  					} else {
   217  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   218  					}
   219  				} else if p.Type == "int64" && *dragonfly {
   220  					if regexp.MustCompile(`^(?i)extp(read|write)`).FindStringSubmatch(funct) == nil {
   221  						args = append(args, "0")
   222  					}
   223  					if endianness == "big-endian" {
   224  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   225  					} else if endianness == "little-endian" {
   226  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   227  					} else {
   228  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   229  					}
   230  				} else if p.Type == "int64" && endianness != "" {
   231  					if len(args)%2 == 1 && *arm {
   232  						// arm abi specifies 64-bit argument uses
   233  						// (even, odd) pair
   234  						args = append(args, "0")
   235  					}
   236  					if endianness == "big-endian" {
   237  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   238  					} else {
   239  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   240  					}
   241  				} else {
   242  					args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   243  				}
   244  			}
   245  
   246  			// Determine which form to use; pad args with zeros.
   247  			asm := "Syscall"
   248  			if nonblock != nil {
   249  				if errvar == "" && goos == "linux" {
   250  					asm = "RawSyscallNoError"
   251  				} else {
   252  					asm = "RawSyscall"
   253  				}
   254  			} else {
   255  				if errvar == "" && goos == "linux" {
   256  					asm = "SyscallNoError"
   257  				}
   258  			}
   259  			if len(args) <= 3 {
   260  				for len(args) < 3 {
   261  					args = append(args, "0")
   262  				}
   263  			} else if len(args) <= 6 {
   264  				asm += "6"
   265  				for len(args) < 6 {
   266  					args = append(args, "0")
   267  				}
   268  			} else if len(args) <= 9 {
   269  				asm += "9"
   270  				for len(args) < 9 {
   271  					args = append(args, "0")
   272  				}
   273  			} else {
   274  				fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct)
   275  			}
   276  
   277  			// System call number.
   278  			if sysname == "" {
   279  				sysname = "SYS_" + funct
   280  				sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
   281  				sysname = strings.ToUpper(sysname)
   282  			}
   283  
   284  			var libcFn string
   285  			if libc {
   286  				asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call
   287  				sysname = strings.TrimPrefix(sysname, "SYS_")         // remove SYS_
   288  				sysname = strings.ToLower(sysname)                    // lowercase
   289  				if sysname == "getdirentries64" {
   290  					// Special case - libSystem name and
   291  					// raw syscall name don't match.
   292  					sysname = "__getdirentries64"
   293  				}
   294  				libcFn = sysname
   295  				sysname = "funcPC(libc_" + sysname + "_trampoline)"
   296  			}
   297  
   298  			// Actual call.
   299  			arglist := strings.Join(args, ", ")
   300  			call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist)
   301  
   302  			// Assign return values.
   303  			body := ""
   304  			ret := []string{"_", "_", "_"}
   305  			doErrno := false
   306  			for i := 0; i < len(out); i++ {
   307  				p := parseParam(out[i])
   308  				reg := ""
   309  				if p.Name == "err" && !*plan9 {
   310  					reg = "e1"
   311  					ret[2] = reg
   312  					doErrno = true
   313  				} else if p.Name == "err" && *plan9 {
   314  					ret[0] = "r0"
   315  					ret[2] = "e1"
   316  					break
   317  				} else {
   318  					reg = fmt.Sprintf("r%d", i)
   319  					ret[i] = reg
   320  				}
   321  				if p.Type == "bool" {
   322  					reg = fmt.Sprintf("%s != 0", reg)
   323  				}
   324  				if p.Type == "int64" && endianness != "" {
   325  					// 64-bit number in r1:r0 or r0:r1.
   326  					if i+2 > len(out) {
   327  						fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct)
   328  					}
   329  					if endianness == "big-endian" {
   330  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
   331  					} else {
   332  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
   333  					}
   334  					ret[i] = fmt.Sprintf("r%d", i)
   335  					ret[i+1] = fmt.Sprintf("r%d", i+1)
   336  				}
   337  				if reg != "e1" || *plan9 {
   338  					body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
   339  				}
   340  			}
   341  			if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
   342  				text += fmt.Sprintf("\t%s\n", call)
   343  			} else {
   344  				if errvar == "" && goos == "linux" {
   345  					// raw syscall without error on Linux, see golang.org/issue/22924
   346  					text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call)
   347  				} else {
   348  					text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
   349  				}
   350  			}
   351  			text += body
   352  
   353  			if *plan9 && ret[2] == "e1" {
   354  				text += "\tif int32(r0) == -1 {\n"
   355  				text += "\t\terr = e1\n"
   356  				text += "\t}\n"
   357  			} else if doErrno {
   358  				text += "\tif e1 != 0 {\n"
   359  				text += "\t\terr = errnoErr(e1)\n"
   360  				text += "\t}\n"
   361  			}
   362  			text += "\treturn\n"
   363  			text += "}\n\n"
   364  
   365  			if libc && !trampolines[libcFn] {
   366  				// some system calls share a trampoline, like read and readlen.
   367  				trampolines[libcFn] = true
   368  				// Declare assembly trampoline.
   369  				text += fmt.Sprintf("func libc_%s_trampoline()\n", libcFn)
   370  				// Assembly trampoline calls the libc_* function, which this magic
   371  				// redirects to use the function from libSystem.
   372  				text += fmt.Sprintf("//go:linkname libc_%s libc_%s\n", libcFn, libcFn)
   373  				text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"/usr/lib/libSystem.B.dylib\"\n", libcFn, libcFn)
   374  				text += "\n"
   375  			}
   376  		}
   377  		if err := s.Err(); err != nil {
   378  			fmt.Fprintf(os.Stderr, err.Error())
   379  			os.Exit(1)
   380  		}
   381  		file.Close()
   382  	}
   383  	fmt.Printf(srcTemplate, cmdLine(), buildTags(), text)
   384  }
   385  
   386  const srcTemplate = `// %s
   387  // Code generated by the command above; see README.md. DO NOT EDIT.
   388  
   389  // +build %s
   390  
   391  package unix
   392  
   393  import (
   394  	"syscall"
   395  	"unsafe"
   396  )
   397  
   398  var _ syscall.Errno
   399  
   400  %s
   401  `