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