// Copyright 2024 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build ignore /* This program must be on the most current zos release. This program generates with data from //\'CEE.SCEELIB\(CELQS003\)\' syscall_zos_s390x.go to output files: zsyscall_zos_s390x.go (generated syscall) zsymaddr_zos_s390x.s (access to the function variable for functions that may not exist) zsysnum_zos_s390x.go (offset from libvec) synopsis: go run ./mksyscall_zos_s390x.go or (with default flags) 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 or if processed on a different platform go run ./mksyscall_zos_s390x.go -i_testfile CELQS003.txt where CELQS003.txt is a text file copy of //\'CEE.SCEELIB\(CELQS003\)\' */ package main import ( "bufio" "flag" "fmt" "io" "log" "os" "os/exec" "path" "regexp" "runtime" "sort" "strconv" "strings" ) var ( sysnumfile = flag.String("o_sysnum", "zsysnum_zos_s390x.go", "zos LE offsets output file in Go") outputgo = flag.String("o_syscall", "zsyscall_zos_s390x.go", "zos generated syscall output file in Go") inputgo = flag.String("i_syscall", "syscall_zos_s390x.go", "zos input file that contain //sys statements") outasm = flag.String("o_asm", "zsymaddr_zos_s390x.s", "zos output file for function variable addresses") testfile = flag.String("i_testfile", "", "file for local validation") ) var copyr = `// %s // Code generated by the command above; see README.md. DO NOT EDIT. //go:build zos && s390x ` var AsmTemplate = ` // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT TEXT ·get_%sAddr(SB), NOSPLIT|NOFRAME, $0-8 MOVD $·%s(SB), R8 MOVD R8, ret+0(FP) RET ` // cmdLine returns this programs's commandline arguments func cmdLine() string { _, fileName, _, _ := runtime.Caller(1) return "go run " + path.Base(fileName) + " -o_sysnum " + *sysnumfile + " -o_syscall " + *outputgo + " -i_syscall " + *inputgo + " -o_asm " + *outasm } func out(ch chan string, file io.ReadCloser) { defer file.Close() defer close(ch) rd := bufio.NewReader(file) loop: for { str, err := rd.ReadString('\n') if err != nil { if err != io.EOF { log.Fatal("Read Error:", err) } break loop } else { ch <- str } } } type SO struct { Symbol string Offset int64 } type SOList []SO func (s SOList) Len() int { return len(s) } func (s SOList) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s SOList) Less(i, j int) bool { if s[i].Offset == s[j].Offset { return s[i].Symbol < s[j].Symbol } return s[i].Offset < s[j].Offset } // Param is function parameter type Param struct { Name string Type string } // parseParamList parses parameter list and returns a slice of parameters func parseParamList(list string) []string { list = strings.TrimSpace(list) if list == "" { return []string{} } return regexp.MustCompile(`\s*,\s*`).Split(list, -1) } // parseParam splits a parameter into name and type func parseParam(p string) Param { ps := regexp.MustCompile(`^(\S*) (\S*)$`).FindStringSubmatch(p) if ps == nil { fmt.Fprintf(os.Stderr, "malformed parameter: %s\n", p) os.Exit(1) } return Param{ps[1], ps[2]} } func main() { flag.Parse() sidedeck := "//'CEE.SCEELIB(CELQS003)'" if *testfile != "" { sidedeck = *testfile } args := []string{"-u", sidedeck} cmd := exec.Command("/bin/cat", args...) stdout, err := cmd.StdoutPipe() if err != nil { println("err stdout ") log.Fatal(err) } c1 := make(chan string) go out(c1, stdout) err2 := cmd.Start() if err2 != nil { log.Fatal(err2) } longest := 0 outstanding := 1 // IMPORT DATA64,CELQV003,'environ',001 r1 := regexp.MustCompile("^ +IMPORT +CODE64,CELQV003,'([A-Za-z_][A-Za-z0-9_]*)',([0-9A-F][0-9A-F][0-9A-F]) *\n$") m := make(map[string]int64) for outstanding > 0 { select { case msg1, ok := <-c1: if ok { result := r1.FindStringSubmatch(msg1) if result != nil { if len(result) > 2 { symbol := "SYS_" + strings.ToUpper(result[1]) offset, e1 := strconv.ParseInt(result[2], 16, 64) if e1 == nil { if len(symbol) > longest { longest = len(symbol) } m[symbol] = offset } else { fmt.Printf("ERROR %s\n", msg1) } } } } else { c1 = nil outstanding-- } } } list := make(SOList, len(m)) i := 0 for k, v := range m { list[i] = SO{k, v} i++ } sort.Sort(list) fmt.Printf("Writing %s\n", *sysnumfile) err = writesysnum(*sysnumfile, &list) if err != nil { fmt.Fprintf(os.Stderr, "Error writesysnum %s %v\n", *sysnumfile, err) os.Exit(1) } err = gofmt(*sysnumfile) if err != nil { fmt.Fprintf(os.Stderr, "Error gofmt %s %v\n", *sysnumfile, err) os.Exit(1) } fmt.Printf("Reading %s\n", *inputgo) f, err := os.Open(*inputgo) if err != nil { fmt.Fprintf(os.Stderr, err.Error()) os.Exit(1) } // open and setup the asm output file fmt.Printf("Writing %s\n", *outasm) fasm, asmerr := os.Create(*outasm) if asmerr != nil { fmt.Fprintf(os.Stderr, "Error open %s %s\n", *outasm, asmerr.Error()) os.Exit(1) } asm := bufio.NewWriter(fasm) fmt.Fprintf(asm, copyr, cmdLine()) fmt.Fprintf(asm, `#include "textflag.h" // provide the address of function variable to be fixed up. `) // open and setup the Go output file fmt.Printf("Writing %s\n", *outputgo) fgo, goerr := os.Create(*outputgo) if goerr != nil { fmt.Fprintf(os.Stderr, "Error open %s %s\n", *outputgo, goerr.Error()) os.Exit(1) } go1 := bufio.NewWriter(fgo) fmt.Fprintf(go1, copyr, cmdLine()) fmt.Fprintf(go1, ` package unix import ( "syscall" "unsafe" "runtime" ) var _ syscall.Errno `) s := bufio.NewScanner(f) scanErr := processStream(s, asm, go1, &m) asm.Flush() fasm.Close() go1.Flush() fgo.Close() err = gofmt(*outputgo) if err != nil { fmt.Fprintf(os.Stderr, "Error gofmt %s %v\n", *outputgo, err) os.Exit(1) } if scanErr != nil { fmt.Fprintf(os.Stderr, "%s", scanErr.Error()) os.Exit(1) } } func writesysnum(file string, l *SOList) error { f, err := os.Create(file) if err != nil { return err } w := bufio.NewWriter(f) defer f.Close() defer w.Flush() fmt.Fprintf(w, copyr, cmdLine()) fmt.Fprintf(w, `package unix const ( `) for _, item := range *l { fmt.Fprintf(w, " %-40s = 0x%X // %d\n", item.Symbol, item.Offset, item.Offset) } fmt.Fprintf(w, ` )`) return nil } func gofmt(file string) error { cmd := exec.Command("gofmt", "-w", file) _, err := cmd.Output() if err != nil { return err } return nil } func processStream(s *bufio.Scanner, asm, go1 *bufio.Writer, m *map[string]int64) error { for s.Scan() { t := s.Text() t = strings.TrimSpace(t) t = regexp.MustCompile(`\s+`).ReplaceAllString(t, ` `) nonblock := regexp.MustCompile(`^\/\/sysnb `).FindStringSubmatch(t) if regexp.MustCompile(`^\/\/sys `).FindStringSubmatch(t) == nil && nonblock == nil { continue } // Line must be of the form // func Open(path string, mode int, perm int) (fd int, errno error) // Split into name, in params, out params. f := regexp.MustCompile(`^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)SYS_[A-Z0-9_]+))?$`).FindStringSubmatch(t) if f == nil { return fmt.Errorf("%s:%s\nmalformed //sys declaration\n", *inputgo, t) } funct, inps, outps, sysname := f[2], f[3], f[4], f[5] if sysname == "" { // if it is empty, it is derived from the function name sysname = "SYS_" + funct sysname = regexp.MustCompile(`([a-z])([A-Z])`).ReplaceAllString(sysname, `${1}_$2`) sysname = strings.ToUpper(sysname) } // Split argument lists on comma. in := parseParamList(inps) out := parseParamList(outps) val, ok := (*m)[sysname] if !ok { return fmt.Errorf("%s:%s\nsysname %s not found on this system\n", *inputgo, s.Text(), sysname) } var newfunc bool if val > 3488 { fmt.Fprintf(asm, AsmTemplate, funct, funct) newfunc = true } else { newfunc = false } // Try in vain to keep people from editing this file. // The theory is that they jump into the middle of the file // without reading the header. text1 := "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n" text2 := "" text3 := "" // Go function header. outDecl := "" if len(out) > 0 { outDecl = fmt.Sprintf(" (%s)", strings.Join(out, ", ")) } if newfunc { text1 += fmt.Sprintf("func impl_%s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) text2 += fmt.Sprintf("//go:nosplit\nfunc get_%sAddr() *(func(%s) %s)\nvar %s = enter_%s\n", funct, strings.Join(in, ", "), outDecl, funct, funct) text2 += fmt.Sprintf("func enter_%s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) text2 += fmt.Sprintf("funcref := get_%sAddr()\n", funct) text2 += fmt.Sprintf("\tif funcptrtest(GetZosLibVec()+%s<<4, \"\") == 0 {\n\t\t*funcref = impl_%s\n", sysname, funct) text2 += fmt.Sprintf("\t} else {\n\t\t*funcref = error_%s\n", funct) text2 += fmt.Sprintf("\t}\n") text3 += fmt.Sprintf("func error_%s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) } else { text1 += fmt.Sprintf("func %s(%s)%s {\n", funct, strings.Join(in, ", "), outDecl) } // Check if err return available errvar := "" for _, param := range out { p := parseParam(param) if p.Type == "error" { errvar = p.Name break } } // Prepare arguments to Syscall. var args []string var fargs []string // for call forwarding n := 0 for _, param := range in { p := parseParam(param) fargs = append(fargs, p.Name) if regexp.MustCompile(`^\*`).FindStringSubmatch(p.Type) != nil { args = append(args, "uintptr(unsafe.Pointer("+p.Name+"))") } else if p.Type == "string" && errvar != "" { text1 += fmt.Sprintf("\tvar _p%d *byte\n", n) text1 += fmt.Sprintf("\t_p%d, %s = BytePtrFromString(%s)\n", n, errvar, p.Name) text1 += fmt.Sprintf("\tif %s != nil {\n\t\treturn\n\t}\n", errvar) args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) n++ } else if p.Type == "string" { fmt.Fprintf(os.Stderr, *inputgo+":"+funct+" uses string arguments, but has no error return\n") text1 += fmt.Sprintf("\tvar _p%d *byte\n", n) text1 += fmt.Sprintf("\t_p%d, _ = BytePtrFromString(%s)\n", n, p.Name) args = append(args, fmt.Sprintf("uintptr(unsafe.Pointer(_p%d))", n)) n++ } else if regexp.MustCompile(`^\[\](.*)`).FindStringSubmatch(p.Type) != nil { // Convert slice into pointer, length. // Have to be careful not to take address of &a[0] if len == 0: // pass dummy pointer in that case. // Used to pass nil, but some OSes or simulators reject write(fd, nil, 0). text1 += fmt.Sprintf("\tvar _p%d unsafe.Pointer\n", n) text1 += fmt.Sprintf("\tif len(%s) > 0 {\n\t\t_p%d = unsafe.Pointer(&%s[0])\n\t}", p.Name, n, p.Name) text1 += fmt.Sprintf(" else {\n\t\t_p%d = unsafe.Pointer(&_zero)\n\t}\n", n) args = append(args, fmt.Sprintf("uintptr(_p%d)", n), fmt.Sprintf("uintptr(len(%s))", p.Name)) n++ } else { args = append(args, fmt.Sprintf("uintptr(%s)", p.Name)) } } // Determine which form to use; pad args with zeros. asmcall := "CallLeFuncWithErr" // Actual call. arglist := strings.Join(args, ", ") call := fmt.Sprintf("%s(GetZosLibVec()+%s<<4, %s)", asmcall, sysname, arglist) // Assign return values. body := "" ret := []string{"_", "_", "_"} doErrno := false for i := 0; i < len(out); i++ { p := parseParam(out[i]) reg := "" if p.Name == "err" { reg = "e1" ret[0] = "r0" ret[1] = "e2" ret[2] = reg doErrno = true } else { reg = fmt.Sprintf("r%d", i) ret[i] = reg } if p.Type == "bool" { reg = fmt.Sprintf("%s != 0", reg) } if reg != "e1" { body += fmt.Sprintf("\t%s = %s(%s)\n", p.Name, p.Type, reg) if newfunc { text3 += fmt.Sprintf("\t%s = -1\n", p.Name) } } } if ret[0] == "_" && ret[1] == "_" && ret[2] == "_" { if nonblock == nil { text1 += fmt.Sprintf("\truntime.EnterSyscall()\n") } text1 += fmt.Sprintf("\t%s\n", call) if nonblock == nil { text1 += fmt.Sprintf("\truntime.ExitSyscall()\n") } text2 += fmt.Sprintf("\t(*funcref)(%s)\n", strings.Join(fargs, ", ")) text2 += "\treturn\n" } else { if nonblock == nil { text1 += fmt.Sprintf("\truntime.EnterSyscall()\n") } text1 += fmt.Sprintf("\t%s, %s, %s := %s\n", ret[0], ret[1], ret[2], call) if nonblock == nil { text1 += fmt.Sprintf("\truntime.ExitSyscall()\n") } text2 += fmt.Sprintf("\treturn (*funcref)(%s)\n", strings.Join(fargs, ", ")) } text1 += body if doErrno { if newfunc { text3 += fmt.Sprintf("\terr = ENOSYS\n") } text1 += "\tif int64(r0) == -1 {\n" text1 += "\t\terr = errnoErr2(e1,e2)\n" text1 += "\t}\n" } if newfunc { text2 += "}\n\n" text3 += "\treturn\n" text3 += "}\n\n" } text1 += "\treturn\n" text1 += "}\n\n" fmt.Fprintf(go1, "%s", text1) if newfunc { fmt.Fprintf(go1, "%s", text2) fmt.Fprintf(go1, "%s", text3) } } if err := s.Err(); err != nil { return err } return nil }