Commit 33267e03 authored by Michael Munday's avatar Michael Munday

unix: add s390x support

This commit adds linux/s390x support to the unix package. It is
based on the changes made to the syscall package in
https://golang.org/cl/20961/. It also adds mkpost.go which is
used to cleanup the API generated by cgo -godefs.

The biggest departure that is made with the syscall package is
the use of the -fsigned-char flag to force signed chars. We
couldn't do this in the syscall package because of the need to
maintain compatibility with the gccgo implementation of the syscall
package (gccgo has supported s390x for a longer time than the Go
toolchain). The unix package does not have this constraint.

Using the -fsigned-char flag makes the API look more like the one
generated on amd64 and arm64 and also more consistent with itself
(the syscall package represents chars using both int8 and uint8
types, the sys package will only ever use int8). Unfortunately it
also means that applications transitioning from the syscall package
to the unix package will see a different API on s390x which might
be confusing. I think the tradeoff is worth it though.

Change-Id: I40b90c18ed787e74ba7a2ebd004bd6bd1ba6279a
Reviewed-on: https://go-review.googlesource.com/23045Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent e82cb4d7
// Copyright 2016 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.
// +build !gccgo
#include "textflag.h"
//
// System calls for s390x, Linux
//
// Just jump to package syscall's implementation for all these functions.
// The runtime may know about them.
TEXT ·Syscall(SB),NOSPLIT,$0-56
BR syscall·Syscall(SB)
TEXT ·Syscall6(SB),NOSPLIT,$0-80
BR syscall·Syscall6(SB)
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
BR syscall·RawSyscall(SB)
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
BR syscall·RawSyscall6(SB)
......@@ -212,6 +212,17 @@ linux_ppc64le)
mksysnum="./mksysnum_linux.pl $unistd_h"
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
;;
linux_s390x)
GOOSARCH_in=syscall_linux_s390x.go
unistd_h=/usr/include/asm/unistd.h
mkerrors="$mkerrors -m64"
mksysnum="./mksysnum_linux.pl $unistd_h"
# Let the type of C char be signed to make the bare sys
# API more consistent between platforms.
# This is a deliberate departure from the way the syscall
# package generates its version of the types file.
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
;;
netbsd_386)
mkerrors="$mkerrors -m32"
mksyscall="./mksyscall.pl -l32 -netbsd"
......@@ -269,6 +280,6 @@ esac
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
if [ -n "$mktypes" ]; then
echo "echo // +build $GOARCH,$GOOS > ztypes_$GOOSARCH.go";
echo "$mktypes types_$GOOS.go | gofmt >>ztypes_$GOOSARCH.go";
echo "$mktypes types_$GOOS.go | go run mkpost.go >>ztypes_$GOOSARCH.go";
fi
) | $run
// Copyright 2016 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.
// +build ignore
// mkpost processes the output of cgo -godefs to
// modify the generated types. It is used to clean up
// the sys API in an architecture specific manner.
//
// mkpost is run after cgo -godefs by mkall.sh.
package main
import (
"fmt"
"go/format"
"io/ioutil"
"log"
"os"
"regexp"
)
func main() {
b, err := ioutil.ReadAll(os.Stdin)
if err != nil {
log.Fatal(err)
}
s := string(b)
goarch := os.Getenv("GOARCH")
goos := os.Getenv("GOOS")
if goarch == "s390x" && goos == "linux" {
// Export the types of PtraceRegs fields.
re := regexp.MustCompile("ptrace(Psw|Fpregs|Per)")
s = re.ReplaceAllString(s, "Ptrace$1")
// Replace padding fields inserted by cgo with blank identifiers.
re = regexp.MustCompile("Pad_cgo[A-Za-z0-9_]*")
s = re.ReplaceAllString(s, "_")
// Replace other unwanted fields with blank identifiers.
re = regexp.MustCompile("X_[A-Za-z0-9_]*")
s = re.ReplaceAllString(s, "_")
// Replace the control_regs union with a blank identifier for now.
re = regexp.MustCompile("(Control_regs)\\s+\\[0\\]uint64")
s = re.ReplaceAllString(s, "_ [0]uint64")
}
// gofmt
b, err = format.Source([]byte(s))
if err != nil {
log.Fatal(err)
}
// Append this command to the header to show where the new file
// came from.
re := regexp.MustCompile("(cgo -godefs [a-zA-Z0-9_]+\\.go.*)")
b = re.ReplaceAll(b, []byte("$1 | go run mkpost.go"))
fmt.Printf("%s", b)
}
// Copyright 2016 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.
package unix
import (
"unsafe"
)
//sys Dup2(oldfd int, newfd int) (err error)
//sys EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error)
//sys Fadvise(fd int, offset int64, length int64, advice int) (err error) = SYS_FADVISE64
//sys Fchown(fd int, uid int, gid int) (err error)
//sys Fstat(fd int, stat *Stat_t) (err error)
//sys Fstatfs(fd int, buf *Statfs_t) (err error)
//sys Ftruncate(fd int, length int64) (err error)
//sysnb Getegid() (egid int)
//sysnb Geteuid() (euid int)
//sysnb Getgid() (gid int)
//sysnb Getrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Getuid() (uid int)
//sysnb InotifyInit() (fd int, err error)
//sys Lchown(path string, uid int, gid int) (err error)
//sys Lstat(path string, stat *Stat_t) (err error)
//sys Pause() (err error)
//sys Pread(fd int, p []byte, offset int64) (n int, err error) = SYS_PREAD64
//sys Pwrite(fd int, p []byte, offset int64) (n int, err error) = SYS_PWRITE64
//sys Seek(fd int, offset int64, whence int) (off int64, err error) = SYS_LSEEK
//sys Select(nfd int, r *FdSet, w *FdSet, e *FdSet, timeout *Timeval) (n int, err error)
//sys sendfile(outfd int, infd int, offset *int64, count int) (written int, err error)
//sys Setfsgid(gid int) (err error)
//sys Setfsuid(uid int) (err error)
//sysnb Setregid(rgid int, egid int) (err error)
//sysnb Setresgid(rgid int, egid int, sgid int) (err error)
//sysnb Setresuid(ruid int, euid int, suid int) (err error)
//sysnb Setrlimit(resource int, rlim *Rlimit) (err error)
//sysnb Setreuid(ruid int, euid int) (err error)
//sys Splice(rfd int, roff *int64, wfd int, woff *int64, len int, flags int) (n int64, err error)
//sys Stat(path string, stat *Stat_t) (err error)
//sys Statfs(path string, buf *Statfs_t) (err error)
//sys SyncFileRange(fd int, off int64, n int64, flags int) (err error)
//sys Truncate(path string, length int64) (err error)
//sysnb getgroups(n int, list *_Gid_t) (nn int, err error)
//sysnb setgroups(n int, list *_Gid_t) (err error)
func Getpagesize() int { return 4096 }
//sysnb Gettimeofday(tv *Timeval) (err error)
func Time(t *Time_t) (tt Time_t, err error) {
var tv Timeval
err = Gettimeofday(&tv)
if err != nil {
return 0, err
}
if t != nil {
*t = Time_t(tv.Sec)
}
return Time_t(tv.Sec), nil
}
//sys Utime(path string, buf *Utimbuf) (err error)
func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }
func NsecToTimespec(nsec int64) (ts Timespec) {
ts.Sec = nsec / 1e9
ts.Nsec = nsec % 1e9
return
}
func NsecToTimeval(nsec int64) (tv Timeval) {
nsec += 999 // round up to microsecond
tv.Sec = nsec / 1e9
tv.Usec = nsec % 1e9 / 1e3
return
}
//sysnb pipe2(p *[2]_C_int, flags int) (err error)
func Pipe(p []int) (err error) {
if len(p) != 2 {
return EINVAL
}
var pp [2]_C_int
err = pipe2(&pp, 0) // pipe2 is the same as pipe when flags are set to 0.
p[0] = int(pp[0])
p[1] = int(pp[1])
return
}
func Pipe2(p []int, flags int) (err error) {
if len(p) != 2 {
return EINVAL
}
var pp [2]_C_int
err = pipe2(&pp, flags)
p[0] = int(pp[0])
p[1] = int(pp[1])
return
}
func Ioperm(from int, num int, on int) (err error) {
return ENOSYS
}
func Iopl(level int) (err error) {
return ENOSYS
}
func (r *PtraceRegs) PC() uint64 { return r.Psw.Addr }
func (r *PtraceRegs) SetPC(pc uint64) { r.Psw.Addr = pc }
func (iov *Iovec) SetLen(length int) {
iov.Len = uint64(length)
}
func (msghdr *Msghdr) SetControllen(length int) {
msghdr.Controllen = uint64(length)
}
func (cmsg *Cmsghdr) SetLen(length int) {
cmsg.Len = uint64(length)
}
// Linux on s390x uses the old mmap interface, which requires arguments to be passed in a struct.
// mmap2 also requires arguments to be passed in a struct; it is currently not exposed in <asm/unistd.h>.
func mmap(addr uintptr, length uintptr, prot int, flags int, fd int, offset int64) (xaddr uintptr, err error) {
mmap_args := [6]uintptr{addr, length, uintptr(prot), uintptr(flags), uintptr(fd), uintptr(offset)}
r0, _, e1 := Syscall(SYS_MMAP, uintptr(unsafe.Pointer(&mmap_args[0])), 0, 0)
use(unsafe.Pointer(&mmap_args[0]))
xaddr = uintptr(r0)
if e1 != 0 {
err = errnoErr(e1)
}
return
}
// On s390x Linux, all the socket calls go through an extra indirection.
// The arguments to the underlying system call (SYS_SOCKETCALL) are the
// number below and a pointer to an array of uintptr.
const (
// see linux/net.h
netSocket = 1
netBind = 2
netConnect = 3
netListen = 4
netAccept = 5
netGetSockName = 6
netGetPeerName = 7
netSocketPair = 8
netSend = 9
netRecv = 10
netSendTo = 11
netRecvFrom = 12
netShutdown = 13
netSetSockOpt = 14
netGetSockOpt = 15
netSendMsg = 16
netRecvMsg = 17
netAccept4 = 18
netRecvMMsg = 19
netSendMMsg = 20
)
func accept(s int, rsa *RawSockaddrAny, addrlen *_Socklen) (int, error) {
args := [3]uintptr{uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))}
fd, _, err := Syscall(SYS_SOCKETCALL, netAccept, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return 0, err
}
return int(fd), nil
}
func accept4(s int, rsa *RawSockaddrAny, addrlen *_Socklen, flags int) (int, error) {
args := [4]uintptr{uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen)), uintptr(flags)}
fd, _, err := Syscall(SYS_SOCKETCALL, netAccept4, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return 0, err
}
return int(fd), nil
}
func getsockname(s int, rsa *RawSockaddrAny, addrlen *_Socklen) error {
args := [3]uintptr{uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))}
_, _, err := RawSyscall(SYS_SOCKETCALL, netGetSockName, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func getpeername(s int, rsa *RawSockaddrAny, addrlen *_Socklen) error {
args := [3]uintptr{uintptr(s), uintptr(unsafe.Pointer(rsa)), uintptr(unsafe.Pointer(addrlen))}
_, _, err := RawSyscall(SYS_SOCKETCALL, netGetPeerName, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func socketpair(domain int, typ int, flags int, fd *[2]int32) error {
args := [4]uintptr{uintptr(domain), uintptr(typ), uintptr(flags), uintptr(unsafe.Pointer(fd))}
_, _, err := RawSyscall(SYS_SOCKETCALL, netSocketPair, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func bind(s int, addr unsafe.Pointer, addrlen _Socklen) error {
args := [3]uintptr{uintptr(s), uintptr(addr), uintptr(addrlen)}
_, _, err := Syscall(SYS_SOCKETCALL, netBind, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func connect(s int, addr unsafe.Pointer, addrlen _Socklen) error {
args := [3]uintptr{uintptr(s), uintptr(addr), uintptr(addrlen)}
_, _, err := Syscall(SYS_SOCKETCALL, netConnect, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func socket(domain int, typ int, proto int) (int, error) {
args := [3]uintptr{uintptr(domain), uintptr(typ), uintptr(proto)}
fd, _, err := RawSyscall(SYS_SOCKETCALL, netSocket, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return 0, err
}
return int(fd), nil
}
func getsockopt(s int, level int, name int, val unsafe.Pointer, vallen *_Socklen) error {
args := [5]uintptr{uintptr(s), uintptr(level), uintptr(name), uintptr(val), uintptr(unsafe.Pointer(vallen))}
_, _, err := Syscall(SYS_SOCKETCALL, netGetSockOpt, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func setsockopt(s int, level int, name int, val unsafe.Pointer, vallen uintptr) error {
args := [4]uintptr{uintptr(s), uintptr(level), uintptr(name), uintptr(val)}
_, _, err := Syscall(SYS_SOCKETCALL, netSetSockOpt, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func recvfrom(s int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (int, error) {
var base uintptr
if len(p) > 0 {
base = uintptr(unsafe.Pointer(&p[0]))
}
args := [6]uintptr{uintptr(s), base, uintptr(len(p)), uintptr(flags), uintptr(unsafe.Pointer(from)), uintptr(unsafe.Pointer(fromlen))}
n, _, err := Syscall(SYS_SOCKETCALL, netRecvFrom, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return 0, err
}
return int(n), nil
}
func sendto(s int, p []byte, flags int, to unsafe.Pointer, addrlen _Socklen) error {
var base uintptr
if len(p) > 0 {
base = uintptr(unsafe.Pointer(&p[0]))
}
args := [6]uintptr{uintptr(s), base, uintptr(len(p)), uintptr(flags), uintptr(to), uintptr(addrlen)}
_, _, err := Syscall(SYS_SOCKETCALL, netSendTo, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func recvmsg(s int, msg *Msghdr, flags int) (int, error) {
args := [3]uintptr{uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)}
n, _, err := Syscall(SYS_SOCKETCALL, netRecvMsg, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return 0, err
}
return int(n), nil
}
func sendmsg(s int, msg *Msghdr, flags int) (int, error) {
args := [3]uintptr{uintptr(s), uintptr(unsafe.Pointer(msg)), uintptr(flags)}
n, _, err := Syscall(SYS_SOCKETCALL, netSendMsg, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return 0, err
}
return int(n), nil
}
func Listen(s int, n int) error {
args := [2]uintptr{uintptr(s), uintptr(n)}
_, _, err := Syscall(SYS_SOCKETCALL, netListen, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
func Shutdown(s, how int) error {
args := [2]uintptr{uintptr(s), uintptr(how)}
_, _, err := Syscall(SYS_SOCKETCALL, netShutdown, uintptr(unsafe.Pointer(&args)), 0)
if err != 0 {
return err
}
return nil
}
......@@ -102,10 +102,22 @@ typedef struct user_pt_regs PtraceRegs;
typedef struct pt_regs PtraceRegs;
#elif defined(__mips__)
typedef struct user PtraceRegs;
#elif defined(__s390x__)
typedef struct _user_regs_struct PtraceRegs;
#else
typedef struct user_regs_struct PtraceRegs;
#endif
#if defined(__s390x__)
typedef struct _user_psw_struct ptracePsw;
typedef struct _user_fpregs_struct ptraceFpregs;
typedef struct _user_per_struct ptracePer;
#else
typedef struct {} ptracePsw;
typedef struct {} ptraceFpregs;
typedef struct {} ptracePer;
#endif
// The real epoll_event is a union, and godefs doesn't handle it well.
struct my_epoll_event {
uint32_t events;
......@@ -113,8 +125,7 @@ struct my_epoll_event {
// padding is not specified in linux/eventpoll.h but added to conform to the
// alignment requirements of EABI
int32_t padFd;
#endif
#ifdef __powerpc64__
#elif defined(__powerpc64__) || defined(__s390x__)
int32_t _padFd;
#endif
int32_t fd;
......@@ -393,6 +404,13 @@ const SizeofInotifyEvent = C.sizeof_struct_inotify_event
// Register structures
type PtraceRegs C.PtraceRegs
// Structures contained in PtraceRegs on s390x (exported by mkpost.go)
type ptracePsw C.ptracePsw
type ptraceFpregs C.ptraceFpregs
type ptracePer C.ptracePer
// Misc
type FdSet C.fd_set
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment