| // 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 |
| } |