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

     1  // Copyright 2024 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 must be on the most current zos release.
     9  
    10  This program generates with data from
    11  
    12  	//\'CEE.SCEELIB\(CELQS003\)\'
    13  	syscall_zos_s390x.go
    14  
    15  to output files:
    16  
    17  	zsyscall_zos_s390x.go (generated syscall)
    18  	zsymaddr_zos_s390x.s  (access to the function variable for functions that may not exist)
    19  	zsysnum_zos_s390x.go  (offset from libvec)
    20  
    21  synopsis:
    22  
    23  	  go run ./mksyscall_zos_s390x.go
    24  
    25  	or (with default flags)
    26  	  go run mksyscall_zos_s390x.go -o_sysnum zsysnum_zos_s390x.go -o_syscall zsyscall_zos_s390x.go -i_syscall syscall_zos_s390x.go -o_asm zsymaddr_zos_s390x.s
    27  
    28  	or if processed on a different platform
    29  	  go run ./mksyscall_zos_s390x.go -i_testfile CELQS003.txt
    30  		where CELQS003.txt is a text file copy of //\'CEE.SCEELIB\(CELQS003\)\'
    31  */
    32  package main
    33  
    34  import (
    35  	"bufio"
    36  	"flag"
    37  	"fmt"
    38  	"io"
    39  	"log"
    40  	"os"
    41  	"os/exec"
    42  	"path"
    43  	"regexp"
    44  	"runtime"
    45  	"sort"
    46  	"strconv"
    47  	"strings"
    48  )
    49  
    50  var (
    51  	sysnumfile = flag.String("o_sysnum", "zsysnum_zos_s390x.go", "zos LE offsets output file in Go")
    52  	outputgo   = flag.String("o_syscall", "zsyscall_zos_s390x.go", "zos generated syscall output file in Go")
    53  	inputgo    = flag.String("i_syscall", "syscall_zos_s390x.go", "zos input file that contain //sys statements")
    54  	outasm     = flag.String("o_asm", "zsymaddr_zos_s390x.s", "zos output file for function variable addresses")
    55  	testfile   = flag.String("i_testfile", "", "file for local validation")
    56  )
    57  var copyr = `// %s
    58  // Code generated by the command above; see README.md. DO NOT EDIT.
    59  
    60  //go:build zos && s390x
    61  `
    62  var AsmTemplate = `
    63  // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
    64  
    65  TEXT ·get_%sAddr(SB), NOSPLIT|NOFRAME, $0-8
    66  	MOVD $·%s(SB), R8
    67  	MOVD R8, ret+0(FP)
    68  	RET
    69  `
    70  
    71  // cmdLine returns this programs's commandline arguments
    72  func cmdLine() string {
    73  	_, fileName, _, _ := runtime.Caller(1)
    74  	return "go run " + path.Base(fileName) + " -o_sysnum " + *sysnumfile + " -o_syscall " + *outputgo + " -i_syscall " + *inputgo + " -o_asm " + *outasm
    75  }
    76  
    77  func out(ch chan string, file io.ReadCloser) {
    78  	defer file.Close()
    79  	defer close(ch)
    80  	rd := bufio.NewReader(file)
    81  loop:
    82  	for {
    83  		str, err := rd.ReadString('\n')
    84  		if err != nil {
    85  			if err != io.EOF {
    86  				log.Fatal("Read Error:", err)
    87  			}
    88  			break loop
    89  		} else {
    90  			ch <- str
    91  		}
    92  	}
    93  }
    94  
    95  type SO struct {
    96  	Symbol string
    97  	Offset int64
    98  }
    99  
   100  type SOList []SO
   101  
   102  func (s SOList) Len() int      { return len(s) }
   103  func (s SOList) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
   104  func (s SOList) Less(i, j int) bool {
   105  	if s[i].Offset == s[j].Offset {
   106  		return s[i].Symbol < s[j].Symbol
   107  	}
   108  	return s[i].Offset < s[j].Offset
   109  }
   110  
   111  // Param is function parameter
   112  type Param struct {
   113  	Name string
   114  	Type string
   115  }
   116  
   117  // parseParamList parses parameter list and returns a slice of parameters
   118  func parseParamList(list string) []string {
   119  	list = strings.TrimSpace(list)
   120  	if list == "" {
   121  		return []string{}
   122  	}
   123  	return regexp.MustCompile(`\s*,\s*`).Split(list, -1)
   124  }
   125  
   126  // parseParam splits a parameter into name and type
   127  func parseParam(p string) Param {
   128  	ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p)
   129  	if ps == nil {
   130  		fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p)
   131  		os.Exit(1)
   132  	}
   133  	return Param{ps[1], ps[2]}
   134  }
   135  
   136  func main() {
   137  	flag.Parse()
   138  	sidedeck := "//'CEE.SCEELIB(CELQS003)'"
   139  	if *testfile != "" {
   140  		sidedeck = *testfile
   141  	}
   142  	args := []string{"-u", sidedeck}
   143  	cmd := exec.Command("/bin/cat", args...)
   144  	stdout, err := cmd.StdoutPipe()
   145  	if err != nil {
   146  		println("err stdout ")
   147  		log.Fatal(err)
   148  	}
   149  	c1 := make(chan string)
   150  	go out(c1, stdout)
   151  	err2 := cmd.Start()
   152  	if err2 != nil {
   153  		log.Fatal(err2)
   154  	}
   155  	longest := 0
   156  	outstanding := 1
   157  	// IMPORT DATA64,CELQV003,'environ',001
   158  	r1 := regexp.MustCompile("^ +IMPORT +CODE64,CELQV003,'([A-Za-z_][A-Za-z0-9_]*)',([0-9A-F][0-9A-F][0-9A-F]) *\n$")
   159  	m := make(map[string]int64)
   160  	for outstanding > 0 {
   161  		select {
   162  		case msg1, ok := <-c1:
   163  			if ok {
   164  				result := r1.FindStringSubmatch(msg1)
   165  				if result != nil {
   166  					if len(result) > 2 {
   167  						symbol := "SYS_" + strings.ToUpper(result[1])
   168  						offset, e1 := strconv.ParseInt(result[2], 16, 64)
   169  						if e1 == nil {
   170  							if len(symbol) > longest {
   171  								longest = len(symbol)
   172  							}
   173  							m[symbol] = offset
   174  						} else {
   175  							fmt.Printf("ERROR %s\n", msg1)
   176  						}
   177  					}
   178  				}
   179  			} else {
   180  				c1 = nil
   181  				outstanding--
   182  			}
   183  
   184  		}
   185  	}
   186  
   187  	list := make(SOList, len(m))
   188  
   189  	i := 0
   190  	for k, v := range m {
   191  		list[i] = SO{k, v}
   192  		i++
   193  	}
   194  	sort.Sort(list)
   195  	fmt.Printf("Writing %s\n", *sysnumfile)
   196  	err = writesysnum(*sysnumfile, &list)
   197  	if err != nil {
   198  		fmt.Fprintf(os.Stderr, "Error writesysnum %s %v\n", *sysnumfile, err)
   199  		os.Exit(1)
   200  	}
   201  	err = gofmt(*sysnumfile)
   202  	if err != nil {
   203  		fmt.Fprintf(os.Stderr, "Error gofmt %s %v\n", *sysnumfile, err)
   204  		os.Exit(1)
   205  	}
   206  
   207  	fmt.Printf("Reading %s\n", *inputgo)
   208  	f, err := os.Open(*inputgo)
   209  	if err != nil {
   210  		fmt.Fprintf(os.Stderr, err.Error())
   211  		os.Exit(1)
   212  	}
   213  
   214  	// open and setup the asm output file
   215  	fmt.Printf("Writing %s\n", *outasm)
   216  	fasm, asmerr := os.Create(*outasm)
   217  	if asmerr != nil {
   218  		fmt.Fprintf(os.Stderr, "Error open %s %s\n", *outasm, asmerr.Error())
   219  		os.Exit(1)
   220  	}
   221  	asm := bufio.NewWriter(fasm)
   222  	fmt.Fprintf(asm, copyr, cmdLine())
   223  	fmt.Fprintf(asm, `#include "textflag.h"
   224  
   225  //  provide the address of function variable to be fixed up.
   226  `)
   227  
   228  	// open and setup the Go output file
   229  	fmt.Printf("Writing %s\n", *outputgo)
   230  	fgo, goerr := os.Create(*outputgo)
   231  	if goerr != nil {
   232  		fmt.Fprintf(os.Stderr, "Error open %s %s\n", *outputgo, goerr.Error())
   233  		os.Exit(1)
   234  	}
   235  	go1 := bufio.NewWriter(fgo)
   236  	fmt.Fprintf(go1, copyr, cmdLine())
   237  	fmt.Fprintf(go1, `
   238  
   239  package unix
   240  
   241  import (
   242  	"syscall"
   243  	"unsafe"
   244  	"runtime"
   245  )
   246  
   247  var _ syscall.Errno
   248  
   249  `)
   250  
   251  	s := bufio.NewScanner(f)
   252  	scanErr := processStream(s, asm, go1, &m)
   253  
   254  	asm.Flush()
   255  	fasm.Close()
   256  
   257  	go1.Flush()
   258  	fgo.Close()
   259  	err = gofmt(*outputgo)
   260  	if err != nil {
   261  		fmt.Fprintf(os.Stderr, "Error gofmt %s %v\n", *outputgo, err)
   262  		os.Exit(1)
   263  	}
   264  	if scanErr != nil {
   265  		fmt.Fprintf(os.Stderr, "%s", scanErr.Error())
   266  		os.Exit(1)
   267  	}
   268  
   269  }
   270  
   271  func writesysnum(file string, l *SOList) error {
   272  	f, err := os.Create(file)
   273  	if err != nil {
   274  		return err
   275  	}
   276  	w := bufio.NewWriter(f)
   277  	defer f.Close()
   278  	defer w.Flush()
   279  	fmt.Fprintf(w, copyr, cmdLine())
   280  	fmt.Fprintf(w, `package unix
   281  const (
   282  `)
   283  	for _, item := range *l {
   284  		fmt.Fprintf(w, "    %-40s = 0x%X   // %d\n", item.Symbol, item.Offset, item.Offset)
   285  	}
   286  	fmt.Fprintf(w, `
   287  )`)
   288  	return nil
   289  }
   290  func gofmt(file string) error {
   291  	cmd := exec.Command("gofmt", "-w", file)
   292  	_, err := cmd.Output()
   293  
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	return nil
   299  }
   300  
   301  func processStream(s *bufio.Scanner, asm, go1 *bufio.Writer, m *map[string]int64) error {
   302  	for s.Scan() {
   303  		t := s.Text()
   304  		t = strings.TrimSpace(t)
   305  		t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `)
   306  		nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t)
   307  		if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil {
   308  			continue
   309  		}
   310  
   311  		// Line must be of the form
   312  		//	func Open(path string, mode int, perm int) (fd int, errno error)
   313  		// Split into name, in params, out params.
   314  		f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t)
   315  		if f == nil {
   316  			return fmt.Errorf("%s:%s\nmalformed //sys declaration\n", *inputgo, t)
   317  		}
   318  		funct, inps, outps, sysname := f[2], f[3], f[4], f[5]
   319  
   320  		if sysname == "" {
   321  			// if it is empty, it is derived from the function name
   322  			sysname = "SYS_" + funct
   323  			sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`)
   324  			sysname = strings.ToUpper(sysname)
   325  		}
   326  
   327  		// Split argument lists on comma.
   328  		in := parseParamList(inps)
   329  		out := parseParamList(outps)
   330  		val, ok := (*m)[sysname]
   331  		if !ok {
   332  			return fmt.Errorf("%s:%s\nsysname %s not found on this system\n", *inputgo, s.Text(), sysname)
   333  		}
   334  		var newfunc bool
   335  		if val > 3488 {
   336  			fmt.Fprintf(asm, AsmTemplate, funct, funct)
   337  			newfunc = true
   338  		} else {
   339  			newfunc = false
   340  		}
   341  		// Try in vain to keep people from editing this file.
   342  		// The theory is that they jump into the middle of the file
   343  		// without reading the header.
   344  		text1 := "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n"
   345  		text2 := ""
   346  		text3 := ""
   347  
   348  		// Go function header.
   349  		outDecl := ""
   350  		if len(out) > 0 {
   351  			outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", "))
   352  		}
   353  		if newfunc {
   354  			text1 += fmt.Sprintf("func impl_%s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   355  			text2 += fmt.Sprintf("//go:nosplit\nfunc get_%sAddr() *(func(%s) %s)\nvar %s = enter_%s\n", funct, strings.Join(in, ", "), outDecl, funct, funct)
   356  			text2 += fmt.Sprintf("func enter_%s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   357  			text2 += fmt.Sprintf("funcref := get_%sAddr()\n", funct)
   358  			text2 += fmt.Sprintf("\tif funcptrtest(GetZosLibVec()+%s<<4, \"\") == 0 {\n\t\t*funcref = impl_%s\n", sysname, funct)
   359  			text2 += fmt.Sprintf("\t} else {\n\t\t*funcref = error_%s\n", funct)
   360  			text2 += fmt.Sprintf("\t}\n")
   361  			text3 += fmt.Sprintf("func error_%s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   362  		} else {
   363  			text1 += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl)
   364  		}
   365  
   366  		// Check if err return available
   367  		errvar := ""
   368  		for _, param := range out {
   369  			p := parseParam(param)
   370  			if p.Type == "error" {
   371  				errvar = p.Name
   372  				break
   373  			}
   374  		}
   375  		// Prepare arguments to Syscall.
   376  		var args []string
   377  		var fargs []string // for call fowarding
   378  		n := 0
   379  		for _, param := range in {
   380  			p := parseParam(param)
   381  			fargs = append(fargs, p.Name)
   382  			if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil {
   383  				args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))")
   384  			} else if p.Type == "string" && errvar != "" {
   385  				text1 += fmt.Sprintf("\tvar _p%d *byte\n", n)
   386  				text1 += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name)
   387  				text1 += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar)
   388  				args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   389  				n++
   390  			} else if p.Type == "string" {
   391  				fmt.Fprintf(os.Stderr, *inputgo+":"+funct+" uses string arguments, but has no error return\n")
   392  				text1 += fmt.Sprintf("\tvar _p%d *byte\n", n)
   393  				text1 += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name)
   394  				args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n))
   395  				n++
   396  			} else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil {
   397  				// Convert slice into pointer, length.
   398  				// Have to be careful not to take address of &a[0] if len == 0:
   399  				// pass dummy pointer in that case.
   400  				// Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
   401  				text1 += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n)
   402  				text1 += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name)
   403  				text1 += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n)
   404  				args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name))
   405  				n++
   406  			} else {
   407  				args = append(args, fmt.Sprintf("uintptr(%s)", p.Name))
   408  			}
   409  		}
   410  
   411  		// Determine which form to use; pad args with zeros.
   412  		asmcall := "CallLeFuncWithErr"
   413  
   414  		// Actual call.
   415  		arglist := strings.Join(args, ", ")
   416  		call := fmt.Sprintf("%s(GetZosLibVec()+%s<<4, %s)", asmcall, sysname, arglist)
   417  
   418  		// Assign return values.
   419  		body := ""
   420  		ret := []string{"_", "_", "_"}
   421  		doErrno := false
   422  		for i := 0; i < len(out); i++ {
   423  			p := parseParam(out[i])
   424  			reg := ""
   425  			if p.Name == "err" {
   426  				reg = "e1"
   427  				ret[0] = "r0"
   428  				ret[1] = "e2"
   429  				ret[2] = reg
   430  				doErrno = true
   431  			} else {
   432  				reg = fmt.Sprintf("r%d", i)
   433  				ret[i] = reg
   434  
   435  			}
   436  			if p.Type == "bool" {
   437  				reg = fmt.Sprintf("%s != 0", reg)
   438  			}
   439  			if reg != "e1" {
   440  				body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg)
   441  				if newfunc {
   442  					text3 += fmt.Sprintf("\t%s = -1\n", p.Name)
   443  				}
   444  			}
   445  		}
   446  		if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" {
   447  			if nonblock == nil {
   448  				text1 += fmt.Sprintf("\truntime.EnterSyscall()\n")
   449  			}
   450  			text1 += fmt.Sprintf("\t%s\n", call)
   451  			if nonblock == nil {
   452  				text1 += fmt.Sprintf("\truntime.ExitSyscall()\n")
   453  			}
   454  			text2 += fmt.Sprintf("\t(*funcref)(%s)\n", strings.Join(fargs, ", "))
   455  			text2 += "\treturn\n"
   456  		} else {
   457  			if nonblock == nil {
   458  				text1 += fmt.Sprintf("\truntime.EnterSyscall()\n")
   459  			}
   460  			text1 += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call)
   461  			if nonblock == nil {
   462  				text1 += fmt.Sprintf("\truntime.ExitSyscall()\n")
   463  			}
   464  			text2 += fmt.Sprintf("\treturn (*funcref)(%s)\n", strings.Join(fargs, ", "))
   465  		}
   466  		text1 += body
   467  
   468  		if doErrno {
   469  			if newfunc {
   470  				text3 += fmt.Sprintf("\terr = ENOSYS\n")
   471  			}
   472  			text1 += "\tif int64(r0) == -1 {\n"
   473  			text1 += "\t\terr = errnoErr2(e1,e2)\n"
   474  			text1 += "\t}\n"
   475  		}
   476  		if newfunc {
   477  			text2 += "}\n\n"
   478  			text3 += "\treturn\n"
   479  			text3 += "}\n\n"
   480  		}
   481  		text1 += "\treturn\n"
   482  		text1 += "}\n\n"
   483  		fmt.Fprintf(go1, "%s", text1)
   484  		if newfunc {
   485  			fmt.Fprintf(go1, "%s", text2)
   486  			fmt.Fprintf(go1, "%s", text3)
   487  		}
   488  
   489  	}
   490  	if err := s.Err(); err != nil {
   491  		return err
   492  	}
   493  	return nil
   494  }