mirror of
https://github.com/golang/sys.git
synced 2026-01-29 07:02:06 +03:00
Change-Id: I40f9866661bad30ee2f4be2e9d0beee54db860b3 Reviewed-on: https://go-review.googlesource.com/c/sys/+/611775 Reviewed-by: Dmitri Shuralyov <dmitshur@google.com> Commit-Queue: Ian Lance Taylor <iant@google.com> Auto-Submit: Dmitri Shuralyov <dmitshur@golang.org> Auto-Submit: Ian Lance Taylor <iant@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
495 lines
13 KiB
Go
495 lines
13 KiB
Go
// 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
|
|
}
|