blob: f4fd945b39d96fd980205926539af2480258fc2f [file] [log] [blame]
// 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 fowarding
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
}