golang.org/x/sys@v0.20.1-0.20240517151509-673e0f94c16d/unix/mksyscall_solaris.go (about)

     1  // Copyright 2019 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  //go:build ignore
     6  
     7  /*
     8   This program reads a file containing function prototypes
     9   (like syscall_solaris.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 err.
    17  	* If go func name needs to be different than its libc name,
    18  	* or the function is not in libc, name could be specified
    19  	* at the end, after "=" sign, like
    20  	  //sys	getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
    21  */
    22  
    23  package main
    24  
    25  import (
    26  	"bufio"
    27  	"flag"
    28  	"fmt"
    29  	"os"
    30  	"regexp"
    31  	"strings"
    32  )
    33  
    34  var (
    35  	b32     = flag.Bool("b32", false, "32bit big-endian")
    36  	l32     = flag.Bool("l32", false, "32bit little-endian")
    37  	tags    = flag.String("tags", "", "build tags")
    38  	illumos = flag.Bool("illumos", false, "illumos specific code generation")
    39  )
    40  
    41  // cmdLine returns this programs's commandline arguments
    42  func cmdLine() string {
    43  	return "go run mksyscall_solaris.go " + strings.Join(os.Args[1:], " ")
    44  }
    45  
    46  // goBuildTags returns build tags in the go:build format.
    47  func goBuildTags() string {
    48  	return strings.ReplaceAll(*tags, ",", " && ")
    49  }
    50  
    51  // Param is function parameter
    52  type Param struct {
    53  	Name string
    54  	Type string
    55  }
    56  
    57  // usage prints the program usage
    58  func usage() {
    59  	fmt.Fprintf(os.Stderr, "usage: go run mksyscall_solaris.go [-b32 | -l32] [-tags x,y] [file ...]\n")
    60  	os.Exit(1)
    61  }
    62  
    63  // parseParamList parses parameter list and returns a slice of parameters
    64  func parseParamList(list string) []string {
    65  	list = strings.TrimSpace(list)
    66  	if list == "" {
    67  		return []string{}
    68  	}
    69  	return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
    70  }
    71  
    72  // parseParam splits a parameter into name and type
    73  func parseParam(p string) Param {
    74  	ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
    75  	if ps == nil {
    76  		fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
    77  		os.Exit(1)
    78  	}
    79  	return Param{ps[1], ps[2]}
    80  }
    81  
    82  func main() {
    83  	flag.Usage = usage
    84  	flag.Parse()
    85  	if len(flag.Args()) <= 0 {
    86  		fmt.Fprintf(os.Stderr, "no files to parse provided\n")
    87  		usage()
    88  	}
    89  
    90  	endianness := ""
    91  	if *b32 {
    92  		endianness = "big-endian"
    93  	} else if *l32 {
    94  		endianness = "little-endian"
    95  	}
    96  
    97  	pack := ""
    98  	text := ""
    99  	dynimports := ""
   100  	linknames := ""
   101  	var vars []string
   102  	for _, path := range flag.Args() {
   103  		file, err := os.Open(path)
   104  		if err != nil {
   105  			fmt.Fprintf(os.Stderr, err.Error())
   106  			os.Exit(1)
   107  		}
   108  		s := bufio.NewScanner(file)
   109  		for s.Scan() {
   110  			t := s.Text()
   111  			if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
   112  				pack = p[1]
   113  			}
   114  			nonblock := regexp.MustCompile(`^\/\/sysnb\t`).FindStringSubmatch(t)
   115  			if regexp.MustCompile(`^\/\/sys\t`).FindStringSubmatch(t) == nil && nonblock == nil {
   116  				continue
   117  			}
   118  
   119  			// Line must be of the form
   120  			//	func Open(path string, mode int, perm int) (fd int, err error)
   121  			// Split into name, in params, out params.
   122  			f := regexp.MustCompile(`^\/\/sys(nb)?\t(\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
   123  			if f == nil {
   124  				fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
   125  				os.Exit(1)
   126  			}
   127  			funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
   128  
   129  			// Split argument lists on comma.
   130  			in := parseParamList(inps)
   131  			out := parseParamList(outps)
   132  
   133  			inps = strings.Join(in, ", ")
   134  			outps = strings.Join(out, ", ")
   135  
   136  			// Try in vain to keep people from editing this file.
   137  			// The theory is that they jump into the middle of the file
   138  			// without reading the header.
   139  			text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
   140  
   141  			// So file name.
   142  			if modname == "" {
   143  				modname = "libc"
   144  			}
   145  
   146  			// System call name.
   147  			if sysname == "" {
   148  				sysname = funct
   149  			}
   150  
   151  			// System call pointer variable name.
   152  			sysvarname := fmt.Sprintf("proc%s", sysname)
   153  
   154  			strconvfunc := "BytePtrFromString"
   155  			strconvtype := "*byte"
   156  
   157  			sysname = strings.ToLower(sysname) // All libc functions are lowercase.
   158  
   159  			if funct != "ioctlPtrRet" {
   160  				// Runtime import of function to allow cross-platform builds.
   161  				dynimports += fmt.Sprintf("//go:cgo_import_dynamic libc_%s %s \"%s.so\"\n", sysname, sysname, modname)
   162  				// Link symbol to proc address variable.
   163  				linknames += fmt.Sprintf("//go:linkname %s libc_%s\n", sysvarname, sysname)
   164  				// Library proc address variable.
   165  				vars = append(vars, sysvarname)
   166  			}
   167  
   168  			// Go function header.
   169  			outlist := strings.Join(out, ", ")
   170  			if outlist != "" {
   171  				outlist = fmt.Sprintf(" (%s)", outlist)
   172  			}
   173  			if text != "" {
   174  				text += "\n"
   175  			}
   176  			text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outlist)
   177  
   178  			// Check if err return available
   179  			errvar := ""
   180  			for _, param := range out {
   181  				p := parseParam(param)
   182  				if p.Type == "error" {
   183  					errvar = p.Name
   184  					continue
   185  				}
   186  			}
   187  
   188  			// Prepare arguments to Syscall.
   189  			var args []string
   190  			n := 0
   191  			for _, param := range in {
   192  				p := parseParam(param)
   193  				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   194  					args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
   195  				} else if p.Type == "string" && errvar != "" {
   196  					text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
   197  					text += fmt.Sprintf("\t_p%d, %s = %s(%s)\n", n, errvar, strconvfunc, p.Name)
   198  					text += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
   199  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   200  					n++
   201  				} else if p.Type == "string" {
   202  					fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
   203  					text += fmt.Sprintf("\tvar _p%d %s\n", n, strconvtype)
   204  					text += fmt.Sprintf("\t_p%d, _ = %s(%s)\n", n, strconvfunc, p.Name)
   205  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   206  					n++
   207  				} else if s := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); s != nil {
   208  					// Convert slice into pointer, length.
   209  					// Have to be careful not to take address of &a[0] if len == 0:
   210  					// pass nil in that case.
   211  					text += fmt.Sprintf("\tvar _p%d *%s\n", n, s[1])
   212  					text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
   213  					args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
   214  					n++
   215  				} else if p.Type == "int64" && endianness != "" {
   216  					if endianness == "big-endian" {
   217  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   218  					} else {
   219  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   220  					}
   221  				} else if p.Type == "bool" {
   222  					text += fmt.Sprintf("\tvar _p%d uint32\n", n)
   223  					text += fmt.Sprintf("\tif %s {\n\t\t_p%d = 1\n\t} else {\n\t\t_p%d = 0\n\t}\n", p.Name, n, n)
   224  					args = append(args, fmt.Sprintf("uintptr(_p%d)", n))
   225  					n++
   226  				} else {
   227  					args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   228  				}
   229  			}
   230  			nargs := len(args)
   231  
   232  			// Determine which form to use; pad args with zeros.
   233  			asm := "sysvicall6"
   234  			if nonblock != nil {
   235  				asm = "rawSysvicall6"
   236  			}
   237  			if len(args) <= 6 {
   238  				for len(args) < 6 {
   239  					args = append(args, "0")
   240  				}
   241  			} else {
   242  				fmt.Fprintf(os.Stderr, "%s: too many arguments to system call\n", path)
   243  				os.Exit(1)
   244  			}
   245  
   246  			// Actual call.
   247  			arglist := strings.Join(args, ", ")
   248  			call := fmt.Sprintf("%s(uintptr(unsafe.Pointer(&%s)), %d, %s)", asm, sysvarname, nargs, arglist)
   249  
   250  			// Assign return values.
   251  			body := ""
   252  			ret := []string{"_", "_", "_"}
   253  			doErrno := false
   254  			for i := 0; i < len(out); i++ {
   255  				p := parseParam(out[i])
   256  				reg := ""
   257  				if p.Name == "err" {
   258  					reg = "e1"
   259  					ret[2] = reg
   260  					doErrno = true
   261  				} else {
   262  					reg = fmt.Sprintf("r%d", i)
   263  					ret[i] = reg
   264  				}
   265  				if p.Type == "bool" {
   266  					reg = fmt.Sprintf("%d != 0", reg)
   267  				}
   268  				if p.Type == "int64" && endianness != "" {
   269  					// 64-bit number in r1:r0 or r0:r1.
   270  					if i+2 > len(out) {
   271  						fmt.Fprintf(os.Stderr, "%s: not enough registers for int64 return\n", path)
   272  						os.Exit(1)
   273  					}
   274  					if endianness == "big-endian" {
   275  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i, i+1)
   276  					} else {
   277  						reg = fmt.Sprintf("int64(r%d)<<32 | int64(r%d)", i+1, i)
   278  					}
   279  					ret[i] = fmt.Sprintf("r%d", i)
   280  					ret[i+1] = fmt.Sprintf("r%d", i+1)
   281  				}
   282  				if reg != "e1" {
   283  					body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
   284  				}
   285  			}
   286  			if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
   287  				text += fmt.Sprintf("\t%s\n", call)
   288  			} else {
   289  				text += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
   290  			}
   291  			text += body
   292  
   293  			if doErrno {
   294  				text += "\tif e1 != 0 {\n"
   295  				text += "\t\terr = errnoErr(e1)\n"
   296  				text += "\t}\n"
   297  			}
   298  			text += "\treturn\n"
   299  			text += "}\n"
   300  		}
   301  		if err := s.Err(); err != nil {
   302  			fmt.Fprintf(os.Stderr, err.Error())
   303  			os.Exit(1)
   304  		}
   305  		file.Close()
   306  	}
   307  	imp := ""
   308  	if pack != "unix" {
   309  		imp = "import \"golang.org/x/sys/unix\"\n"
   310  	}
   311  
   312  	syscallimp := ""
   313  	if !*illumos {
   314  		syscallimp = "\"syscall\""
   315  	}
   316  
   317  	vardecls := "\t" + strings.Join(vars, ",\n\t")
   318  	vardecls += " syscallFunc"
   319  	fmt.Printf(srcTemplate, cmdLine(), goBuildTags(), pack, syscallimp, imp, dynimports, linknames, vardecls, text)
   320  }
   321  
   322  const srcTemplate = `// %s
   323  // Code generated by the command above; see README.md. DO NOT EDIT.
   324  
   325  //go:build %s
   326  
   327  package %s
   328  
   329  import (
   330          "unsafe"
   331          %s
   332  )
   333  %s
   334  %s
   335  %s
   336  var (
   337  %s	
   338  )
   339  
   340  %s
   341  `