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