github.com/Andyfoo/golang/x/sys@v0.0.0-20190901054642-57c1bf301704/unix/mksyscall_aix_ppc.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_aix.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  package main
    23  
    24  import (
    25  	"bufio"
    26  	"flag"
    27  	"fmt"
    28  	"os"
    29  	"regexp"
    30  	"strings"
    31  )
    32  
    33  var (
    34  	b32  = flag.Bool("b32", false, "32bit big-endian")
    35  	l32  = flag.Bool("l32", false, "32bit little-endian")
    36  	aix  = flag.Bool("aix", false, "aix")
    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_aix_ppc.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_aix_ppc.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  	cExtern := "/*\n#include <stdint.h>\n#include <stddef.h>\n"
    99  	for _, path := range flag.Args() {
   100  		file, err := os.Open(path)
   101  		if err != nil {
   102  			fmt.Fprintf(os.Stderr, err.Error())
   103  			os.Exit(1)
   104  		}
   105  		s := bufio.NewScanner(file)
   106  		for s.Scan() {
   107  			t := s.Text()
   108  			t = strings.TrimSpace(t)
   109  			t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
   110  			if p := regexp.MustCompile(`^package (\S+)$`).FindStringSubmatch(t); p != nil && pack == "" {
   111  				pack = p[1]
   112  			}
   113  			nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
   114  			if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
   115  				continue
   116  			}
   117  
   118  			// Line must be of the form
   119  			//	func Open(path string, mode int, perm int) (fd int, err error)
   120  			// Split into name, in params, out params.
   121  			f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$`).FindStringSubmatch(t)
   122  			if f == nil {
   123  				fmt.Fprintf(os.Stderr, "%s:%s\nmalformed //sys declaration\n", path, t)
   124  				os.Exit(1)
   125  			}
   126  			funct, inps, outps, modname, sysname := f[2], f[3], f[4], f[5], f[6]
   127  
   128  			// Split argument lists on comma.
   129  			in := parseParamList(inps)
   130  			out := parseParamList(outps)
   131  
   132  			inps = strings.Join(in, ", ")
   133  			outps = strings.Join(out, ", ")
   134  
   135  			// Try in vain to keep people from editing this file.
   136  			// The theory is that they jump into the middle of the file
   137  			// without reading the header.
   138  			text += "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
   139  
   140  			// Check if value return, err return available
   141  			errvar := ""
   142  			retvar := ""
   143  			rettype := ""
   144  			for _, param := range out {
   145  				p := parseParam(param)
   146  				if p.Type == "error" {
   147  					errvar = p.Name
   148  				} else {
   149  					retvar = p.Name
   150  					rettype = p.Type
   151  				}
   152  			}
   153  
   154  			// System call name.
   155  			if sysname == "" {
   156  				sysname = funct
   157  			}
   158  			sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
   159  			sysname = strings.ToLower(sysname) // All libc functions are lowercase.
   160  
   161  			cRettype := ""
   162  			if rettype == "unsafe.Pointer" {
   163  				cRettype = "uintptr_t"
   164  			} else if rettype == "uintptr" {
   165  				cRettype = "uintptr_t"
   166  			} else if regexp.MustCompile(`^_`).FindStringSubmatch(rettype) != nil {
   167  				cRettype = "uintptr_t"
   168  			} else if rettype == "int" {
   169  				cRettype = "int"
   170  			} else if rettype == "int32" {
   171  				cRettype = "int"
   172  			} else if rettype == "int64" {
   173  				cRettype = "long long"
   174  			} else if rettype == "uint32" {
   175  				cRettype = "unsigned int"
   176  			} else if rettype == "uint64" {
   177  				cRettype = "unsigned long long"
   178  			} else {
   179  				cRettype = "int"
   180  			}
   181  			if sysname == "exit" {
   182  				cRettype = "void"
   183  			}
   184  
   185  			// Change p.Types to c
   186  			var cIn []string
   187  			for _, param := range in {
   188  				p := parseParam(param)
   189  				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   190  					cIn = append(cIn, "uintptr_t")
   191  				} else if p.Type == "string" {
   192  					cIn = append(cIn, "uintptr_t")
   193  				} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
   194  					cIn = append(cIn, "uintptr_t", "size_t")
   195  				} else if p.Type == "unsafe.Pointer" {
   196  					cIn = append(cIn, "uintptr_t")
   197  				} else if p.Type == "uintptr" {
   198  					cIn = append(cIn, "uintptr_t")
   199  				} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
   200  					cIn = append(cIn, "uintptr_t")
   201  				} else if p.Type == "int" {
   202  					cIn = append(cIn, "int")
   203  				} else if p.Type == "int32" {
   204  					cIn = append(cIn, "int")
   205  				} else if p.Type == "int64" {
   206  					cIn = append(cIn, "long long")
   207  				} else if p.Type == "uint32" {
   208  					cIn = append(cIn, "unsigned int")
   209  				} else if p.Type == "uint64" {
   210  					cIn = append(cIn, "unsigned long long")
   211  				} else {
   212  					cIn = append(cIn, "int")
   213  				}
   214  			}
   215  
   216  			if funct != "fcntl" && funct != "FcntlInt" && funct != "readlen" && funct != "writelen" {
   217  				if sysname == "select" {
   218  					// select is a keyword of Go. Its name is
   219  					// changed to c_select.
   220  					cExtern += "#define c_select select\n"
   221  				}
   222  				// Imports of system calls from libc
   223  				cExtern += fmt.Sprintf("%s %s", cRettype, sysname)
   224  				cIn := strings.Join(cIn, ", ")
   225  				cExtern += fmt.Sprintf("(%s);\n", cIn)
   226  			}
   227  
   228  			// So file name.
   229  			if *aix {
   230  				if modname == "" {
   231  					modname = "libc.a/shr_64.o"
   232  				} else {
   233  					fmt.Fprintf(os.Stderr, "%s: only syscall using libc are available\n", funct)
   234  					os.Exit(1)
   235  				}
   236  			}
   237  
   238  			strconvfunc := "C.CString"
   239  
   240  			// Go function header.
   241  			if outps != "" {
   242  				outps = fmt.Sprintf(" (%s)", outps)
   243  			}
   244  			if text != "" {
   245  				text += "\n"
   246  			}
   247  
   248  			text += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outps)
   249  
   250  			// Prepare arguments to Syscall.
   251  			var args []string
   252  			n := 0
   253  			argN := 0
   254  			for _, param := range in {
   255  				p := parseParam(param)
   256  				if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   257  					args = append(args, "C.uintptr_t(uintptr(unsafe.Pointer("+p.Name+")))")
   258  				} else if p.Type == "string" && errvar != "" {
   259  					text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name)
   260  					args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n))
   261  					n++
   262  				} else if p.Type == "string" {
   263  					fmt.Fprintf(os.Stderr, path+":"+funct+" uses string arguments, but has no error return\n")
   264  					text += fmt.Sprintf("\t_p%d := uintptr(unsafe.Pointer(%s(%s)))\n", n, strconvfunc, p.Name)
   265  					args = append(args, fmt.Sprintf("C.uintptr_t(_p%d)", n))
   266  					n++
   267  				} else if m := regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type); m != nil {
   268  					// Convert slice into pointer, length.
   269  					// Have to be careful not to take address of &a[0] if len == 0:
   270  					// pass nil in that case.
   271  					text += fmt.Sprintf("\tvar _p%d *%s\n", n, m[1])
   272  					text += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = &%s[0]\n\t}\n", p.Name, n, p.Name)
   273  					args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(unsafe.Pointer(_p%d)))", n))
   274  					n++
   275  					text += fmt.Sprintf("\tvar _p%d int\n", n)
   276  					text += fmt.Sprintf("\t_p%d = len(%s)\n", n, p.Name)
   277  					args = append(args, fmt.Sprintf("C.size_t(_p%d)", n))
   278  					n++
   279  				} else if p.Type == "int64" && endianness != "" {
   280  					if endianness == "big-endian" {
   281  						args = append(args, fmt.Sprintf("uintptr(%s>>32)", p.Name), fmt.Sprintf("uintptr(%s)", p.Name))
   282  					} else {
   283  						args = append(args, fmt.Sprintf("uintptr(%s)", p.Name), fmt.Sprintf("uintptr(%s>>32)", p.Name))
   284  					}
   285  					n++
   286  				} else if p.Type == "bool" {
   287  					text += fmt.Sprintf("\tvar _p%d uint32\n", n)
   288  					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)
   289  					args = append(args, fmt.Sprintf("_p%d", n))
   290  				} else if regexp.MustCompile(`^_`).FindStringSubmatch(p.Type) != nil {
   291  					args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name))
   292  				} else if p.Type == "unsafe.Pointer" {
   293  					args = append(args, fmt.Sprintf("C.uintptr_t(uintptr(%s))", p.Name))
   294  				} else if p.Type == "int" {
   295  					if (argN == 2) && ((funct == "readlen") || (funct == "writelen")) {
   296  						args = append(args, fmt.Sprintf("C.size_t(%s)", p.Name))
   297  					} else if argN == 0 && funct == "fcntl" {
   298  						args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
   299  					} else if (argN == 2) && ((funct == "fcntl") || (funct == "FcntlInt")) {
   300  						args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
   301  					} else {
   302  						args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
   303  					}
   304  				} else if p.Type == "int32" {
   305  					args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
   306  				} else if p.Type == "int64" {
   307  					args = append(args, fmt.Sprintf("C.longlong(%s)", p.Name))
   308  				} else if p.Type == "uint32" {
   309  					args = append(args, fmt.Sprintf("C.uint(%s)", p.Name))
   310  				} else if p.Type == "uint64" {
   311  					args = append(args, fmt.Sprintf("C.ulonglong(%s)", p.Name))
   312  				} else if p.Type == "uintptr" {
   313  					args = append(args, fmt.Sprintf("C.uintptr_t(%s)", p.Name))
   314  				} else {
   315  					args = append(args, fmt.Sprintf("C.int(%s)", p.Name))
   316  				}
   317  				argN++
   318  			}
   319  
   320  			// Actual call.
   321  			arglist := strings.Join(args, ", ")
   322  			call := ""
   323  			if sysname == "exit" {
   324  				if errvar != "" {
   325  					call += "er :="
   326  				} else {
   327  					call += ""
   328  				}
   329  			} else if errvar != "" {
   330  				call += "r0,er :="
   331  			} else if retvar != "" {
   332  				call += "r0,_ :="
   333  			} else {
   334  				call += ""
   335  			}
   336  			if sysname == "select" {
   337  				// select is a keyword of Go. Its name is
   338  				// changed to c_select.
   339  				call += fmt.Sprintf("C.c_%s(%s)", sysname, arglist)
   340  			} else {
   341  				call += fmt.Sprintf("C.%s(%s)", sysname, arglist)
   342  			}
   343  
   344  			// Assign return values.
   345  			body := ""
   346  			for i := 0; i < len(out); i++ {
   347  				p := parseParam(out[i])
   348  				reg := ""
   349  				if p.Name == "err" {
   350  					reg = "e1"
   351  				} else {
   352  					reg = "r0"
   353  				}
   354  				if reg != "e1" {
   355  					body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
   356  				}
   357  			}
   358  
   359  			// verify return
   360  			if sysname != "exit" && errvar != "" {
   361  				if regexp.MustCompile(`^uintptr`).FindStringSubmatch(cRettype) != nil {
   362  					body += "\tif (uintptr(r0) ==^uintptr(0) && er != nil) {\n"
   363  					body += fmt.Sprintf("\t\t%s = er\n", errvar)
   364  					body += "\t}\n"
   365  				} else {
   366  					body += "\tif (r0 ==-1 && er != nil) {\n"
   367  					body += fmt.Sprintf("\t\t%s = er\n", errvar)
   368  					body += "\t}\n"
   369  				}
   370  			} else if errvar != "" {
   371  				body += "\tif (er != nil) {\n"
   372  				body += fmt.Sprintf("\t\t%s = er\n", errvar)
   373  				body += "\t}\n"
   374  			}
   375  
   376  			text += fmt.Sprintf("\t%s\n", call)
   377  			text += body
   378  
   379  			text += "\treturn\n"
   380  			text += "}\n"
   381  		}
   382  		if err := s.Err(); err != nil {
   383  			fmt.Fprintf(os.Stderr, err.Error())
   384  			os.Exit(1)
   385  		}
   386  		file.Close()
   387  	}
   388  	imp := ""
   389  	if pack != "unix" {
   390  		imp = "import \"github.com/Andyfoo/golang/x/sys/unix\"\n"
   391  
   392  	}
   393  	fmt.Printf(srcTemplate, cmdLine(), buildTags(), pack, cExtern, imp, text)
   394  }
   395  
   396  const srcTemplate = `// %s
   397  // Code generated by the command above; see README.md. DO NOT EDIT.
   398  
   399  // +build %s
   400  
   401  package %s
   402  
   403  
   404  %s
   405  */
   406  import "C"
   407  import (
   408  	"unsafe"
   409  )
   410  
   411  
   412  %s
   413  
   414  %s
   415  `