github.com/go-xe2/third@v1.0.3/golang.org/x/sys/plan9/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_plan9.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  			// Split argument lists on comma.
   153  			in := parseParamList(inps)
   154  			out := parseParamList(outps)
   155  
   156  			// Try in vain to keep people from editing this file.
   157  			// The theory is that they jump into the middle of the file
   158  			// without reading the header.
   159  			text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
   160  
   161  			// Go function header.
   162  			outDecl := ""
   163  			if len(out) > 0 {
   164  				outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", "))
   165  			}
   166  			text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   167  
   168  			// Check if err return available
   169  			errvar := ""
   170  			for _, param := range out {
   171  				p := parseParam(param)
   172  				if p.Type == "error" {
   173  					errvar = p.Name
   174  					break
   175  				}
   176  			}
   177  
   178  			// Prepare arguments to Syscall.
   179  			var args []string
   180  			n := 0
   181  			for _, param := range in {
   182  				p := parseParam(param)
   183  				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   184  					args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
   185  				} else if p.Type == "string" && errvar != "" {
   186  					text += fmt.Sprintf("\tvar _p%d *byte\n", n)
   187  					text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name)
   188  					text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
   189  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   190  					n++
   191  				} else if p.Type == "string" {
   192  					fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
   193  					text += fmt.Sprintf("\tvar _p%d *byte\n", n)
   194  					text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name)
   195  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   196  					n++
   197  				} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
   198  					// Convert slice into pointer, length.
   199  					// Have to be careful not to take address of &a[0] if len == 0:
   200  					// pass dummy pointer in that case.
   201  					// Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
   202  					text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n)
   203  					text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name)
   204  					text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n)
   205  					args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
   206  					n++
   207  				} else if p.Type == "int64" && (*openbsd || *netbsd) {
   208  					args = append(args, "0")
   209  					if endianness == "big-endian" {
   210  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   211  					} else if endianness == "little-endian" {
   212  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   213  					} else {
   214  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   215  					}
   216  				} else if p.Type == "int64" && *dragonfly {
   217  					if regexp.MustCompile(`^(?i)extp(read|write)`).FindStringSubmatch(funct) == nil {
   218  						args = append(args, "0")
   219  					}
   220  					if endianness == "big-endian" {
   221  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   222  					} else if endianness == "little-endian" {
   223  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   224  					} else {
   225  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   226  					}
   227  				} else if p.Type == "int64" && endianness != "" {
   228  					if len(args)%2 == 1 && *arm {
   229  						// arm abi specifies 64-bit argument uses
   230  						// (even, odd) pair
   231  						args = append(args, "0")
   232  					}
   233  					if endianness == "big-endian" {
   234  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   235  					} else {
   236  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   237  					}
   238  				} else {
   239  					args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   240  				}
   241  			}
   242  
   243  			// Determine which form to use; pad args with zeros.
   244  			asm := "Syscall"
   245  			if nonblock != nil {
   246  				if errvar == "" && goos == "linux" {
   247  					asm = "RawSyscallNoError"
   248  				} else {
   249  					asm = "RawSyscall"
   250  				}
   251  			} else {
   252  				if errvar == "" && goos == "linux" {
   253  					asm = "SyscallNoError"
   254  				}
   255  			}
   256  			if len(args) <= 3 {
   257  				for len(args) < 3 {
   258  					args = append(args, "0")
   259  				}
   260  			} else if len(args) <= 6 {
   261  				asm += "6"
   262  				for len(args) < 6 {
   263  					args = append(args, "0")
   264  				}
   265  			} else if len(args) <= 9 {
   266  				asm += "9"
   267  				for len(args) < 9 {
   268  					args = append(args, "0")
   269  				}
   270  			} else {
   271  				fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct)
   272  			}
   273  
   274  			// System call number.
   275  			if sysname == "" {
   276  				sysname = "SYS_" + funct
   277  				sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
   278  				sysname = strings.ToUpper(sysname)
   279  			}
   280  
   281  			var libcFn string
   282  			if libc {
   283  				asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call
   284  				sysname = strings.TrimPrefix(sysname, "SYS_")         // remove SYS_
   285  				sysname = strings.ToLower(sysname)                    // lowercase
   286  				if sysname == "getdirentries64" {
   287  					// Special case - libSystem name and
   288  					// raw syscall name don't match.
   289  					sysname = "__getdirentries64"
   290  				}
   291  				libcFn = sysname
   292  				sysname = "funcPC(libc_" + sysname + "_trampoline)"
   293  			}
   294  
   295  			// Actual call.
   296  			arglist := strings.Join(args, ", ")
   297  			call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist)
   298  
   299  			// Assign return values.
   300  			body := ""
   301  			ret := []string{"_", "_", "_"}
   302  			doErrno := false
   303  			for i := 0; i < len(out); i++ {
   304  				p := parseParam(out[i])
   305  				reg := ""
   306  				if p.Name == "err" && !*plan9 {
   307  					reg = "e1"
   308  					ret[2] = reg
   309  					doErrno = true
   310  				} else if p.Name == "err" && *plan9 {
   311  					ret[0] = "r0"
   312  					ret[2] = "e1"
   313  					break
   314  				} else {
   315  					reg = fmt.Sprintf("r%d", i)
   316  					ret[i] = reg
   317  				}
   318  				if p.Type == "bool" {
   319  					reg = fmt.Sprintf("%s != 0", reg)
   320  				}
   321  				if p.Type == "int64" && endianness != "" {
   322  					// 64-bit number in r1:r0 or r0:r1.
   323  					if i+2 > len(out) {
   324  						fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct)
   325  					}
   326  					if endianness == "big-endian" {
   327  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
   328  					} else {
   329  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
   330  					}
   331  					ret[i] = fmt.Sprintf("r%d", i)
   332  					ret[i+1] = fmt.Sprintf("r%d", i+1)
   333  				}
   334  				if reg != "e1" || *plan9 {
   335  					body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
   336  				}
   337  			}
   338  			if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
   339  				text += fmt.Sprintf("\t%s\n", call)
   340  			} else {
   341  				if errvar == "" && goos == "linux" {
   342  					// raw syscall without error on Linux, see golang.org/issue/22924
   343  					text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call)
   344  				} else {
   345  					text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
   346  				}
   347  			}
   348  			text += body
   349  
   350  			if *plan9 && ret[2] == "e1" {
   351  				text += "\tif int32(r0) == -1 {\n"
   352  				text += "\t\terr = e1\n"
   353  				text += "\t}\n"
   354  			} else if doErrno {
   355  				text += "\tif e1 != 0 {\n"
   356  				text += "\t\terr = errnoErr(e1)\n"
   357  				text += "\t}\n"
   358  			}
   359  			text += "\treturn\n"
   360  			text += "}\n\n"
   361  
   362  			if libc && !trampolines[libcFn] {
   363  				// some system calls share a trampoline, like read and readlen.
   364  				trampolines[libcFn] = true
   365  				// Declare assembly trampoline.
   366  				text += fmt.Sprintf("func libc_%s_trampoline()\n", libcFn)
   367  				// Assembly trampoline calls the libc_* function, which this magic
   368  				// redirects to use the function from libSystem.
   369  				text += fmt.Sprintf("//go:linkname libc_%s libc_%s\n", libcFn, libcFn)
   370  				text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"/usr/lib/libSystem.B.dylib\"\n", libcFn, libcFn)
   371  				text += "\n"
   372  			}
   373  		}
   374  		if err := s.Err(); err != nil {
   375  			fmt.Fprintf(os.Stderr, err.Error())
   376  			os.Exit(1)
   377  		}
   378  		file.Close()
   379  	}
   380  	fmt.Printf(srcTemplate, cmdLine(), buildTags(), text)
   381  }
   382  
   383  const srcTemplate = `// %s
   384  // Code generated by the command above; see README.md. DO NOT EDIT.
   385  
   386  // +build %s
   387  
   388  package plan9
   389  
   390  import "unsafe"
   391  
   392  %s
   393  `