github.com/xingly-cn/shorturl-go@v0.0.0-20220110130535-e21de4659f74/pkg/mod/golang.org/x/sys@v0.0.0-20200323222414-85ca7c5b95cd/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  	if goos == "" {
    92  		fmt.Fprintln(os.Stderr, "GOOS not defined in environment")
    93  		os.Exit(1)
    94  	}
    95  	goarch := os.Getenv("GOARCH_TARGET")
    96  	if goarch == "" {
    97  		goarch = os.Getenv("GOARCH")
    98  	}
    99  
   100  	// Check that we are using the Docker-based build system if we should
   101  	if goos == "linux" {
   102  		if os.Getenv("GOLANG_SYS_BUILD") != "docker" {
   103  			fmt.Fprintf(os.Stderr, "In the Docker-based build system, mksyscall should not be called directly.\n")
   104  			fmt.Fprintf(os.Stderr, "See README.md\n")
   105  			os.Exit(1)
   106  		}
   107  	}
   108  
   109  	flag.Usage = usage
   110  	flag.Parse()
   111  	if len(flag.Args()) <= 0 {
   112  		fmt.Fprintf(os.Stderr, "no files to parse provided\n")
   113  		usage()
   114  	}
   115  
   116  	endianness := ""
   117  	if *b32 {
   118  		endianness = "big-endian"
   119  	} else if *l32 {
   120  		endianness = "little-endian"
   121  	}
   122  
   123  	libc := false
   124  	if goos == "darwin" && (strings.Contains(buildTags(), ",go1.12") || strings.Contains(buildTags(), ",go1.13")) {
   125  		libc = true
   126  	}
   127  	trampolines := map[string]bool{}
   128  
   129  	text := ""
   130  	for _, path := range flag.Args() {
   131  		file, err := os.Open(path)
   132  		if err != nil {
   133  			fmt.Fprintf(os.Stderr, err.Error())
   134  			os.Exit(1)
   135  		}
   136  		s := bufio.NewScanner(file)
   137  		for s.Scan() {
   138  			t := s.Text()
   139  			t = strings.TrimSpace(t)
   140  			t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
   141  			nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
   142  			if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
   143  				continue
   144  			}
   145  
   146  			// Line must be of the form
   147  			//	func Open(path string, mode int, perm int) (fd int, errno error)
   148  			// Split into name, in params, out params.
   149  			f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t)
   150  			if f == nil {
   151  				fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
   152  				os.Exit(1)
   153  			}
   154  			funct, inps, outps, sysname := f[2], f[3], f[4], f[5]
   155  
   156  			// ClockGettime doesn't have a syscall number on Darwin, only generate libc wrappers.
   157  			if goos == "darwin" && !libc && funct == "ClockGettime" {
   158  				continue
   159  			}
   160  
   161  			// Split argument lists on comma.
   162  			in := parseParamList(inps)
   163  			out := parseParamList(outps)
   164  
   165  			// Try in vain to keep people from editing this file.
   166  			// The theory is that they jump into the middle of the file
   167  			// without reading the header.
   168  			text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
   169  
   170  			// Go function header.
   171  			outDecl := ""
   172  			if len(out) > 0 {
   173  				outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", "))
   174  			}
   175  			text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   176  
   177  			// Check if err return available
   178  			errvar := ""
   179  			for _, param := range out {
   180  				p := parseParam(param)
   181  				if p.Type == "error" {
   182  					errvar = p.Name
   183  					break
   184  				}
   185  			}
   186  
   187  			// Prepare arguments to Syscall.
   188  			var args []string
   189  			n := 0
   190  			for _, param := range in {
   191  				p := parseParam(param)
   192  				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   193  					args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
   194  				} else if p.Type == "string" && errvar != "" {
   195  					text += fmt.Sprintf("\tvar _p%d *byte\n", n)
   196  					text += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name)
   197  					text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
   198  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   199  					n++
   200  				} else if p.Type == "string" {
   201  					fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
   202  					text += fmt.Sprintf("\tvar _p%d *byte\n", n)
   203  					text += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name)
   204  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   205  					n++
   206  				} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
   207  					// Convert slice into pointer, length.
   208  					// Have to be careful not to take address of &a[0] if len == 0:
   209  					// pass dummy pointer in that case.
   210  					// Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
   211  					text += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n)
   212  					text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name)
   213  					text += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n)
   214  					args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
   215  					n++
   216  				} else if p.Type == "int64" && (*openbsd || *netbsd) {
   217  					args = append(args, "0")
   218  					if endianness == "big-endian" {
   219  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   220  					} else if endianness == "little-endian" {
   221  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   222  					} else {
   223  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   224  					}
   225  				} else if p.Type == "int64" && *dragonfly {
   226  					if regexp.MustCompile(`^(?i)extp(read|write)`).FindStringSubmatch(funct) == nil {
   227  						args = append(args, "0")
   228  					}
   229  					if endianness == "big-endian" {
   230  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   231  					} else if endianness == "little-endian" {
   232  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   233  					} else {
   234  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   235  					}
   236  				} else if (p.Type == "int64" || p.Type == "uint64") && endianness != "" {
   237  					if len(args)%2 == 1 && *arm {
   238  						// arm abi specifies 64-bit argument uses
   239  						// (even, odd) pair
   240  						args = append(args, "0")
   241  					}
   242  					if endianness == "big-endian" {
   243  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   244  					} else {
   245  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   246  					}
   247  				} else {
   248  					args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   249  				}
   250  			}
   251  
   252  			// Determine which form to use; pad args with zeros.
   253  			asm := "Syscall"
   254  			if nonblock != nil {
   255  				if errvar == "" && goos == "linux" {
   256  					asm = "RawSyscallNoError"
   257  				} else {
   258  					asm = "RawSyscall"
   259  				}
   260  			} else {
   261  				if errvar == "" && goos == "linux" {
   262  					asm = "SyscallNoError"
   263  				}
   264  			}
   265  			if len(args) <= 3 {
   266  				for len(args) < 3 {
   267  					args = append(args, "0")
   268  				}
   269  			} else if len(args) <= 6 {
   270  				asm += "6"
   271  				for len(args) < 6 {
   272  					args = append(args, "0")
   273  				}
   274  			} else if len(args) <= 9 {
   275  				asm += "9"
   276  				for len(args) < 9 {
   277  					args = append(args, "0")
   278  				}
   279  			} else {
   280  				fmt.Fprintf(os.Stderr, "%s:%s too many arguments to system call\n", path, funct)
   281  			}
   282  
   283  			// System call number.
   284  			if sysname == "" {
   285  				sysname = "SYS_" + funct
   286  				sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
   287  				sysname = strings.ToUpper(sysname)
   288  			}
   289  
   290  			var libcFn string
   291  			if libc {
   292  				asm = "syscall_" + strings.ToLower(asm[:1]) + asm[1:] // internal syscall call
   293  				sysname = strings.TrimPrefix(sysname, "SYS_")         // remove SYS_
   294  				sysname = strings.ToLower(sysname)                    // lowercase
   295  				libcFn = sysname
   296  				sysname = "funcPC(libc_" + sysname + "_trampoline)"
   297  			}
   298  
   299  			// Actual call.
   300  			arglist := strings.Join(args, ", ")
   301  			call := fmt.Sprintf("%s(%s, %s)", asm, sysname, arglist)
   302  
   303  			// Assign return values.
   304  			body := ""
   305  			ret := []string{"_", "_", "_"}
   306  			doErrno := false
   307  			for i := 0; i < len(out); i++ {
   308  				p := parseParam(out[i])
   309  				reg := ""
   310  				if p.Name == "err" && !*plan9 {
   311  					reg = "e1"
   312  					ret[2] = reg
   313  					doErrno = true
   314  				} else if p.Name == "err" && *plan9 {
   315  					ret[0] = "r0"
   316  					ret[2] = "e1"
   317  					break
   318  				} else {
   319  					reg = fmt.Sprintf("r%d", i)
   320  					ret[i] = reg
   321  				}
   322  				if p.Type == "bool" {
   323  					reg = fmt.Sprintf("%s != 0", reg)
   324  				}
   325  				if p.Type == "int64" && endianness != "" {
   326  					// 64-bit number in r1:r0 or r0:r1.
   327  					if i+2 > len(out) {
   328  						fmt.Fprintf(os.Stderr, "%s:%s not enough registers for int64 return\n", path, funct)
   329  					}
   330  					if endianness == "big-endian" {
   331  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
   332  					} else {
   333  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
   334  					}
   335  					ret[i] = fmt.Sprintf("r%d", i)
   336  					ret[i+1] = fmt.Sprintf("r%d", i+1)
   337  				}
   338  				if reg != "e1" || *plan9 {
   339  					body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
   340  				}
   341  			}
   342  			if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
   343  				text += fmt.Sprintf("\t%s\n", call)
   344  			} else {
   345  				if errvar == "" && goos == "linux" {
   346  					// raw syscall without error on Linux, see golang.org/issue/22924
   347  					text += fmt.Sprintf("\t%s, %s := %s\n", ret[0], ret[1], call)
   348  				} else {
   349  					text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
   350  				}
   351  			}
   352  			text += body
   353  
   354  			if *plan9 && ret[2] == "e1" {
   355  				text += "\tif int32(r0) == -1 {\n"
   356  				text += "\t\terr = e1\n"
   357  				text += "\t}\n"
   358  			} else if doErrno {
   359  				text += "\tif e1 != 0 {\n"
   360  				text += "\t\terr = errnoErr(e1)\n"
   361  				text += "\t}\n"
   362  			}
   363  			text += "\treturn\n"
   364  			text += "}\n\n"
   365  
   366  			if libc && !trampolines[libcFn] {
   367  				// some system calls share a trampoline, like read and readlen.
   368  				trampolines[libcFn] = true
   369  				// Declare assembly trampoline.
   370  				text += fmt.Sprintf("func libc_%s_trampoline()\n", libcFn)
   371  				// Assembly trampoline calls the libc_* function, which this magic
   372  				// redirects to use the function from libSystem.
   373  				text += fmt.Sprintf("//go:linkname libc_%s libc_%s\n", libcFn, libcFn)
   374  				text += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"/usr/lib/libSystem.B.dylib\"\n", libcFn, libcFn)
   375  				text += "\n"
   376  			}
   377  		}
   378  		if err := s.Err(); err != nil {
   379  			fmt.Fprintf(os.Stderr, err.Error())
   380  			os.Exit(1)
   381  		}
   382  		file.Close()
   383  	}
   384  	fmt.Printf(srcTemplate, cmdLine(), buildTags(), text)
   385  }
   386  
   387  const srcTemplate = `// %s
   388  // Code generated by the command above; see README.md. DO NOT EDIT.
   389  
   390  // +build %s
   391  
   392  package unix
   393  
   394  import (
   395  	"syscall"
   396  	"unsafe"
   397  )
   398  
   399  var _ syscall.Errno
   400  
   401  %s
   402  `