build: separate compiler and libs

This commit is contained in:
Li Jie
2025-01-07 21:49:08 +08:00
parent b0123567cd
commit 1172e5bdce
559 changed files with 190 additions and 176 deletions

View File

@@ -0,0 +1,174 @@
// Copyright 2010 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 unix || (js && wasm) || plan9 || wasip1
// Unix environment variables.
package syscall
import (
"runtime"
"sync"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
var (
// envOnce guards initialization by copyenv, which populates env.
envOnce sync.Once
// envLock guards env and envs.
envLock sync.RWMutex
// env maps from an environment variable to its first occurrence in envs.
env map[string]int
// envs is provided by the runtime. elements are expected to
// be of the form "key=value". An empty string means deleted
// (or a duplicate to be ignored).
envs []string = runtimeEnvs()
)
func runtimeEnvs() []string {
ret := make([]string, 0, 8)
cenvs := os.Environ
i := 0
for {
ce := *c.Advance(cenvs, i)
if ce == nil {
return ret
}
ret = append(ret, c.GoString(ce))
i++
}
}
func copyenv() {
env = make(map[string]int)
for i, s := range envs {
for j := 0; j < len(s); j++ {
if s[j] == '=' {
key := s[:j]
if _, ok := env[key]; !ok {
env[key] = i // first mention of key
} else {
// Clear duplicate keys. This permits Unsetenv to
// safely delete only the first item without
// worrying about unshadowing a later one,
// which might be a security problem.
envs[i] = ""
}
break
}
}
}
}
func Unsetenv(key string) error {
envOnce.Do(copyenv)
envLock.Lock()
defer envLock.Unlock()
if i, ok := env[key]; ok {
envs[i] = ""
delete(env, key)
}
os.Unsetenv(c.AllocaCStr(key))
return nil
}
func Getenv(key string) (value string, found bool) {
envOnce.Do(copyenv)
if len(key) == 0 {
return "", false
}
envLock.RLock()
defer envLock.RUnlock()
i, ok := env[key]
if !ok {
return "", false
}
s := envs[i]
for i := 0; i < len(s); i++ {
if s[i] == '=' {
return s[i+1:], true
}
}
return "", false
}
/* TODO(xsw):
func Getenv(key string) (value string, found bool) {
ret := os.Getenv(c.AllocaCStr(key))
if ret != nil {
return c.GoString(ret), true
}
return "", false
}
*/
func Setenv(key, value string) error {
envOnce.Do(copyenv)
if len(key) == 0 {
return Errno(syscall.EINVAL)
}
for i := 0; i < len(key); i++ {
if key[i] == '=' || key[i] == 0 {
return Errno(syscall.EINVAL)
}
}
// On Plan 9, null is used as a separator, eg in $path.
if runtime.GOOS != "plan9" {
for i := 0; i < len(value); i++ {
if value[i] == 0 {
return Errno(syscall.EINVAL)
}
}
}
envLock.Lock()
defer envLock.Unlock()
i, ok := env[key]
kv := key + "=" + value
if ok {
envs[i] = kv
} else {
i = len(envs)
envs = append(envs, kv)
}
env[key] = i
os.Setenv(c.AllocaCStr(key), c.AllocaCStr(value), 1)
return nil
}
func Clearenv() {
envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
envLock.Lock()
defer envLock.Unlock()
os.Clearenv()
env = make(map[string]int)
envs = []string{}
}
func Environ() []string {
envOnce.Do(copyenv)
envLock.RLock()
defer envLock.RUnlock()
a := make([]string, 0, len(envs))
for _, env := range envs {
if env != "" {
a = append(a, env)
}
}
return a
}

View File

@@ -0,0 +1,96 @@
// Copyright 2010 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.
// Windows environment variables.
package syscall
import (
"unsafe"
)
func Getenv(key string) (value string, found bool) {
keyp, err := UTF16PtrFromString(key)
if err != nil {
return "", false
}
n := uint32(100)
for {
b := make([]uint16, n)
n, err = GetEnvironmentVariable(keyp, &b[0], uint32(len(b)))
if n == 0 && err == ERROR_ENVVAR_NOT_FOUND {
return "", false
}
if n <= uint32(len(b)) {
return UTF16ToString(b[:n]), true
}
}
}
func Setenv(key, value string) error {
v, err := UTF16PtrFromString(value)
if err != nil {
return err
}
keyp, err := UTF16PtrFromString(key)
if err != nil {
return err
}
e := SetEnvironmentVariable(keyp, v)
if e != nil {
return e
}
runtimeSetenv(key, value)
return nil
}
func Unsetenv(key string) error {
keyp, err := UTF16PtrFromString(key)
if err != nil {
return err
}
e := SetEnvironmentVariable(keyp, nil)
if e != nil {
return e
}
runtimeUnsetenv(key)
return nil
}
func Clearenv() {
for _, s := range Environ() {
// Environment variables can begin with =
// so start looking for the separator = at j=1.
// https://devblogs.microsoft.com/oldnewthing/20100506-00/?p=14133
for j := 1; j < len(s); j++ {
if s[j] == '=' {
Unsetenv(s[0:j])
break
}
}
}
}
func Environ() []string {
envp, e := GetEnvironmentStrings()
if e != nil {
return nil
}
defer FreeEnvironmentStrings(envp)
r := make([]string, 0, 50) // Empty with room to grow.
const size = unsafe.Sizeof(*envp)
for *envp != 0 { // environment block ends with empty string
// find NUL terminator
end := unsafe.Pointer(envp)
for *(*uint16)(end) != 0 {
end = unsafe.Add(end, size)
}
entry := unsafe.Slice(envp, (uintptr(end)-uintptr(unsafe.Pointer(envp)))/size)
r = append(r, UTF16ToString(entry))
envp = (*uint16)(unsafe.Add(end, size))
}
return r
}

View File

@@ -0,0 +1,321 @@
// Copyright 2011 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 aix || solaris
// This file handles forkAndExecInChild function for OS using libc syscall like AIX or Solaris.
package syscall
import (
"runtime"
"unsafe"
"github.com/goplus/llgo/c"
)
type SysProcAttr struct {
Chroot string // Chroot.
Credential *Credential // Credential.
Setsid bool // Create session.
// Setpgid sets the process group ID of the child to Pgid,
// or, if Pgid == 0, to the new child's process ID.
Setpgid bool
// Setctty sets the controlling terminal of the child to
// file descriptor Ctty. Ctty must be a descriptor number
// in the child process: an index into ProcAttr.Files.
// This is only meaningful if Setsid is true.
Setctty bool
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd
// Foreground places the child process group in the foreground.
// This implies Setpgid. The Ctty field must be set to
// the descriptor of the controlling TTY.
// Unlike Setctty, in this case Ctty must be a descriptor
// number in the parent process.
Foreground bool
Pgid int // Child's process group ID if Setpgid.
}
// Implemented in runtime package.
func runtime_BeforeFork()
func runtime_AfterFork()
func runtime_AfterForkInChild()
func chdir(path uintptr) (err Errno)
func chroot1(path uintptr) (err Errno)
func closeFD(fd uintptr) (err Errno)
func dup2child(old uintptr, new uintptr) (val uintptr, err Errno)
func execve(path uintptr, argv uintptr, envp uintptr) (err Errno)
func exit(code uintptr)
func fcntl1(fd uintptr, cmd uintptr, arg uintptr) (val uintptr, err Errno)
func forkx(flags uintptr) (pid uintptr, err Errno)
func getpid() (pid uintptr, err Errno)
func ioctl(fd uintptr, req uintptr, arg uintptr) (err Errno)
func setgid(gid uintptr) (err Errno)
func setgroups1(ngid uintptr, gid uintptr) (err Errno)
func setrlimit1(which uintptr, lim unsafe.Pointer) (err Errno)
func setsid() (pid uintptr, err Errno)
func setuid(uid uintptr) (err Errno)
func setpgid(pid uintptr, pgid uintptr) (err Errno)
func write1(fd uintptr, buf uintptr, nbyte uintptr) (n uintptr, err Errno)
// syscall defines this global on our behalf to avoid a build dependency on other platforms
func init() {
execveLibc = execve
}
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
// If a dup or exec fails, write the errno error to pipe.
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
// In the child, this function must not acquire any locks, because
// they might have been locked at the time of the fork. This means
// no rescheduling, no malloc calls, and no new stack segments.
//
// We call hand-crafted syscalls, implemented in
// ../runtime/syscall_solaris.go, rather than generated libc wrappers
// because we need to avoid lazy-loading the functions (might malloc,
// split the stack, or acquire mutexes). We can't call RawSyscall
// because it's not safe even for BSD-subsystem calls.
//
// func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
var (
r1 uintptr
err1 Errno
nextfd int
i int
pgrp _Pid_t
cred *Credential
ngroups, groups uintptr
)
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
// guard against side effects of shuffling fds below.
// Make sure that nextfd is beyond any currently open files so
// that we can't run the risk of overwriting any of them.
fd := make([]int, len(attr.Files))
nextfd = len(attr.Files)
for i, ufd := range attr.Files {
if nextfd < int(ufd) {
nextfd = int(ufd)
}
fd[i] = int(ufd)
}
nextfd++
// About to call fork.
// No more allocation or calls of non-assembly functions.
runtime_BeforeFork()
r1, err1 = forkx(0x1) // FORK_NOSIGCHLD
if err1 != 0 {
runtime_AfterFork()
return 0, err1
}
if r1 != 0 {
// parent; return PID
runtime_AfterFork()
return int(r1), 0
}
// Fork succeeded, now in child.
// Session ID
if sys.Setsid {
_, err1 = setsid()
if err1 != 0 {
goto childerror
}
}
// Set process group
if sys.Setpgid || sys.Foreground {
// Place child in process group.
err1 = setpgid(0, uintptr(sys.Pgid))
if err1 != 0 {
goto childerror
}
}
if sys.Foreground {
pgrp = _Pid_t(sys.Pgid)
if pgrp == 0 {
r1, err1 = getpid()
if err1 != 0 {
goto childerror
}
pgrp = _Pid_t(r1)
}
// Place process group in foreground.
err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
}
// Restore the signal mask. We do this after TIOCSPGRP to avoid
// having the kernel send a SIGTTOU signal to the process group.
runtime_AfterForkInChild()
// Chroot
if chroot != nil {
err1 = chroot1(uintptr(unsafe.Pointer(chroot)))
if err1 != 0 {
goto childerror
}
}
// User and groups
if cred = sys.Credential; cred != nil {
ngroups = uintptr(len(cred.Groups))
groups = uintptr(0)
if ngroups > 0 {
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
}
if !cred.NoSetGroups {
err1 = setgroups1(ngroups, groups)
if err1 != 0 {
goto childerror
}
}
err1 = setgid(uintptr(cred.Gid))
if err1 != 0 {
goto childerror
}
err1 = setuid(uintptr(cred.Uid))
if err1 != 0 {
goto childerror
}
}
// Chdir
if dir != nil {
err1 = chdir(uintptr(unsafe.Pointer(dir)))
if err1 != 0 {
goto childerror
}
}
// Pass 1: look for fd[i] < i and move those up above len(fd)
// so that pass 2 won't stomp on an fd it needs later.
if pipe < nextfd {
switch runtime.GOOS {
case "illumos", "solaris":
_, err1 = fcntl1(uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
default:
_, err1 = dup2child(uintptr(pipe), uintptr(nextfd))
if err1 != 0 {
goto childerror
}
_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
}
if err1 != 0 {
goto childerror
}
pipe = nextfd
nextfd++
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < i {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
switch runtime.GOOS {
case "illumos", "solaris":
_, err1 = fcntl1(uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
default:
_, err1 = dup2child(uintptr(fd[i]), uintptr(nextfd))
if err1 != 0 {
goto childerror
}
_, err1 = fcntl1(uintptr(nextfd), F_SETFD, FD_CLOEXEC)
}
if err1 != 0 {
goto childerror
}
fd[i] = nextfd
nextfd++
}
}
// Pass 2: dup fd[i] down onto i.
for i = 0; i < len(fd); i++ {
if fd[i] == -1 {
closeFD(uintptr(i))
continue
}
if fd[i] == i {
// dup2(i, i) won't clear close-on-exec flag on Linux,
// probably not elsewhere either.
_, err1 = fcntl1(uintptr(fd[i]), F_SETFD, 0)
if err1 != 0 {
goto childerror
}
continue
}
// The new fd is created NOT close-on-exec,
// which is exactly what we want.
_, err1 = dup2child(uintptr(fd[i]), uintptr(i))
if err1 != 0 {
goto childerror
}
}
// By convention, we don't close-on-exec the fds we are
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
// Programs that know they inherit fds >= 3 will need
// to set them close-on-exec.
for i = len(fd); i < 3; i++ {
closeFD(uintptr(i))
}
// Detach fd 0 from tty
if sys.Noctty {
err1 = ioctl(0, uintptr(TIOCNOTTY), 0)
if err1 != 0 {
goto childerror
}
}
// Set the controlling TTY to Ctty
if sys.Setctty {
// On AIX, TIOCSCTTY is undefined
if TIOCSCTTY == 0 {
err1 = ENOSYS
goto childerror
}
err1 = ioctl(uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
}
// Restore original rlimit.
if rlimOK && rlim.Cur != 0 {
setrlimit1(RLIMIT_NOFILE, unsafe.Pointer(&rlim))
}
// Time to exec.
err1 = execve(
uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&envv[0])))
childerror:
// send error code on pipe
write1(uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
for {
exit(253)
}
}
func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
return ioctl(fd, req, uintptr(arg))
}

View File

@@ -0,0 +1,329 @@
// Copyright 2011 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 darwin || (openbsd && !mips64)
package syscall
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
type SysProcAttr struct {
Chroot string // Chroot.
Credential *Credential // Credential.
Ptrace bool // Enable tracing.
Setsid bool // Create session.
// Setpgid sets the process group ID of the child to Pgid,
// or, if Pgid == 0, to the new child's process ID.
Setpgid bool
// Setctty sets the controlling terminal of the child to
// file descriptor Ctty. Ctty must be a descriptor number
// in the child process: an index into ProcAttr.Files.
// This is only meaningful if Setsid is true.
Setctty bool
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd
// Foreground places the child process group in the foreground.
// This implies Setpgid. The Ctty field must be set to
// the descriptor of the controlling TTY.
// Unlike Setctty, in this case Ctty must be a descriptor
// number in the parent process.
Foreground bool
Pgid int // Child's process group ID if Setpgid.
}
/* TODO(xsw):
// Implemented in runtime package.
func runtime_BeforeFork()
func runtime_AfterFork()
func runtime_AfterForkInChild()
*/
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
// If a dup or exec fails, write the errno error to pipe.
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
// In the child, this function must not acquire any locks, because
// they might have been locked at the time of the fork. This means
// no rescheduling, no malloc calls, and no new stack segments.
// For the same reason compiler does not race instrument it.
// The calls to rawSyscall are okay because they are assembly
// functions that do not grow the stack.
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err1 Errno) {
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err1).
var (
r1 uintptr
nextfd int
i int
cred *Credential
// err error
// pgrp c.Int
// ngroups, groups uintptr
)
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
// guard against side effects of shuffling fds below.
// Make sure that nextfd is beyond any currently open files so
// that we can't run the risk of overwriting any of them.
fd := make([]int, len(attr.Files))
nextfd = len(attr.Files)
for i, ufd := range attr.Files {
if nextfd < int(ufd) {
nextfd = int(ufd)
}
fd[i] = int(ufd)
}
nextfd++
// About to call fork.
// No more allocation or calls of non-assembly functions.
// runtime_BeforeFork()
r1, err1 = fork()
if err1 != 0 {
// runtime_AfterFork()
return 0, err1
}
if r1 != 0 {
// parent; return PID
// runtime_AfterFork()
return int(r1), 0
}
// Fork succeeded, now in child.
// TODO(xsw): check this
// Enable tracing if requested.
if sys.Ptrace {
/* TODO(xsw):
if err = ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
err1 = err.(Errno)
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Ptrace")
}
// Session ID
if sys.Setsid {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setsid_trampoline), 0, 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Setsid")
}
// Set process group
if sys.Setpgid || sys.Foreground {
/* TODO(xsw):
// Place child in process group.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Setpgid")
}
if sys.Foreground {
/* TODO(xsw):
// This should really be pid_t, however _C_int (aka int32) is
// generally equivalent.
pgrp = _C_int(sys.Pgid)
if pgrp == 0 {
r1, _, err1 = rawSyscall(abi.FuncPCABI0(libc_getpid_trampoline), 0, 0, 0)
if err1 != 0 {
goto childerror
}
pgrp = _C_int(r1)
}
// Place process group in foreground.
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Foreground")
}
// Restore the signal mask. We do this after TIOCSPGRP to avoid
// having the kernel send a SIGTTOU signal to the process group.
// runtime_AfterForkInChild()
// Chroot
if chroot != nil {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chroot_trampoline), uintptr(unsafe.Pointer(chroot)), 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - chroot")
}
// User and groups
if cred = sys.Credential; cred != nil {
/* TODO(xsw):
ngroups = uintptr(len(cred.Groups))
groups = uintptr(0)
if ngroups > 0 {
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
}
if !cred.NoSetGroups {
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setgroups_trampoline), ngroups, groups, 0)
if err1 != 0 {
goto childerror
}
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setgid_trampoline), uintptr(cred.Gid), 0, 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setuid_trampoline), uintptr(cred.Uid), 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Credential")
}
// Chdir
if dir != nil {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - dir")
}
// Pass 1: look for fd[i] < i and move those up above len(fd)
// so that pass 2 won't stomp on an fd it needs later.
if pipe < nextfd {
/* TODO(xsw):
if runtime.GOOS == "openbsd" {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
} else {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(pipe), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC)
}
if err1 != 0 {
goto childerror
}
pipe = nextfd
nextfd++
*/
panic("todo: syscall.forkAndExecInChild - pipe < nextfd")
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < i {
/* TODO(xsw):
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
if runtime.GOOS == "openbsd" {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
} else {
_, _, err1 = rawSyscall(dupTrampoline, uintptr(fd[i]), uintptr(nextfd), 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_fcntl_trampoline), uintptr(nextfd), F_SETFD, FD_CLOEXEC)
}
if err1 != 0 {
goto childerror
}
fd[i] = nextfd
nextfd++
*/
panic("todo: syscall.forkAndExecInChild - for fd")
}
}
// Pass 2: dup fd[i] down onto i.
for i = 0; i < len(fd); i++ {
if fd[i] == -1 {
os.Close(c.Int(i))
continue
}
if fd[i] == i {
// dup2(i, i) won't clear close-on-exec flag on Linux,
// probably not elsewhere either.
if ret := os.Fcntl(c.Int(fd[i]), syscall.F_SETFD, 0); ret < 0 {
err1 = Errno(os.Errno())
goto childerror
}
continue
}
// The new fd is created NOT close-on-exec,
if ret := os.Dup2(c.Int(fd[i]), c.Int(i)); ret < 0 {
err1 = Errno(os.Errno())
goto childerror
}
}
// By convention, we don't close-on-exec the fds we are
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
// Programs that know they inherit fds >= 3 will need
// to set them close-on-exec.
for i = len(fd); i < 3; i++ {
os.Close(c.Int(i))
}
// Detach fd 0 from tty
if sys.Noctty {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), 0, uintptr(TIOCNOTTY), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Noctty")
}
// Set the controlling TTY to Ctty
if sys.Setctty {
/* TODO(xsw):
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
if err1 != 0 {
goto childerror
}
*/
panic("todo: syscall.forkAndExecInChild - sys.Setctty")
}
// Restore original rlimit.
if rlimOK && rlim.Cur != 0 {
os.Setrlimit(syscall.RLIMIT_NOFILE, (*syscall.Rlimit)(&rlim))
}
// Time to exec.
os.Execve(argv0, argv, envv)
/* TODO(xsw):
ret := os.Execve(argv0, argv, envv)
if ret != 0 {
err1 = Errno(ret)
}
*/
childerror:
// send error code on pipe
os.Write(c.Int(pipe), unsafe.Pointer(&err1), unsafe.Sizeof(err1))
for {
os.Exit(253)
}
}

View File

@@ -0,0 +1,735 @@
// Copyright 2011 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 linux
package syscall
import "github.com/goplus/llgo/c"
// Linux unshare/clone/clone2/clone3 flags, architecture-independent,
// copied from linux/sched.h.
const (
CLONE_VM = 0x00000100 // set if VM shared between processes
CLONE_FS = 0x00000200 // set if fs info shared between processes
CLONE_FILES = 0x00000400 // set if open files shared between processes
CLONE_SIGHAND = 0x00000800 // set if signal handlers and blocked signals shared
CLONE_PIDFD = 0x00001000 // set if a pidfd should be placed in parent
CLONE_PTRACE = 0x00002000 // set if we want to let tracing continue on the child too
CLONE_VFORK = 0x00004000 // set if the parent wants the child to wake it up on mm_release
CLONE_PARENT = 0x00008000 // set if we want to have the same parent as the cloner
CLONE_THREAD = 0x00010000 // Same thread group?
CLONE_NEWNS = 0x00020000 // New mount namespace group
CLONE_SYSVSEM = 0x00040000 // share system V SEM_UNDO semantics
CLONE_SETTLS = 0x00080000 // create a new TLS for the child
CLONE_PARENT_SETTID = 0x00100000 // set the TID in the parent
CLONE_CHILD_CLEARTID = 0x00200000 // clear the TID in the child
CLONE_DETACHED = 0x00400000 // Unused, ignored
CLONE_UNTRACED = 0x00800000 // set if the tracing process can't force CLONE_PTRACE on this clone
CLONE_CHILD_SETTID = 0x01000000 // set the TID in the child
CLONE_NEWCGROUP = 0x02000000 // New cgroup namespace
CLONE_NEWUTS = 0x04000000 // New utsname namespace
CLONE_NEWIPC = 0x08000000 // New ipc namespace
CLONE_NEWUSER = 0x10000000 // New user namespace
CLONE_NEWPID = 0x20000000 // New pid namespace
CLONE_NEWNET = 0x40000000 // New network namespace
CLONE_IO = 0x80000000 // Clone io context
// Flags for the clone3() syscall.
CLONE_CLEAR_SIGHAND = 0x100000000 // Clear any signal handler and reset to SIG_DFL.
CLONE_INTO_CGROUP = 0x200000000 // Clone into a specific cgroup given the right permissions.
// Cloning flags intersect with CSIGNAL so can be used with unshare and clone3
// syscalls only:
CLONE_NEWTIME = 0x00000080 // New time namespace
)
// SysProcIDMap holds Container ID to Host ID mappings used for User Namespaces in Linux.
// See user_namespaces(7).
type SysProcIDMap struct {
ContainerID int // Container ID.
HostID int // Host ID.
Size int // Size.
}
type SysProcAttr struct {
Chroot string // Chroot.
Credential *Credential // Credential.
// Ptrace tells the child to call ptrace(PTRACE_TRACEME).
// Call runtime.LockOSThread before starting a process with this set,
// and don't call UnlockOSThread until done with PtraceSyscall calls.
Ptrace bool
Setsid bool // Create session.
// Setpgid sets the process group ID of the child to Pgid,
// or, if Pgid == 0, to the new child's process ID.
Setpgid bool
// Setctty sets the controlling terminal of the child to
// file descriptor Ctty. Ctty must be a descriptor number
// in the child process: an index into ProcAttr.Files.
// This is only meaningful if Setsid is true.
Setctty bool
Noctty bool // Detach fd 0 from controlling terminal
Ctty int // Controlling TTY fd
// Foreground places the child process group in the foreground.
// This implies Setpgid. The Ctty field must be set to
// the descriptor of the controlling TTY.
// Unlike Setctty, in this case Ctty must be a descriptor
// number in the parent process.
Foreground bool
Pgid int // Child's process group ID if Setpgid.
// Pdeathsig, if non-zero, is a signal that the kernel will send to
// the child process when the creating thread dies. Note that the signal
// is sent on thread termination, which may happen before process termination.
// There are more details at https://go.dev/issue/27505.
Pdeathsig Signal
Cloneflags uintptr // Flags for clone calls (Linux only)
Unshareflags uintptr // Flags for unshare calls (Linux only)
UidMappings []SysProcIDMap // User ID mappings for user namespaces.
GidMappings []SysProcIDMap // Group ID mappings for user namespaces.
// GidMappingsEnableSetgroups enabling setgroups syscall.
// If false, then setgroups syscall will be disabled for the child process.
// This parameter is no-op if GidMappings == nil. Otherwise for unprivileged
// users this should be set to false for mappings work.
GidMappingsEnableSetgroups bool
AmbientCaps []uintptr // Ambient capabilities (Linux only)
UseCgroupFD bool // Whether to make use of the CgroupFD field.
CgroupFD int // File descriptor of a cgroup to put the new process into.
}
var (
none = [...]byte{'n', 'o', 'n', 'e', 0}
slash = [...]byte{'/', 0}
)
/* TODO(xsw):
// Implemented in runtime package.
func runtime_BeforeFork()
func runtime_AfterFork()
func runtime_AfterForkInChild()
*/
// Fork, dup fd onto 0..len(fd), and exec(argv0, argvv, envv) in child.
// If a dup or exec fails, write the errno error to pipe.
// (Pipe is close-on-exec so if exec succeeds, it will be closed.)
// In the child, this function must not acquire any locks, because
// they might have been locked at the time of the fork. This means
// no rescheduling, no malloc calls, and no new stack segments.
// For the same reason compiler does not race instrument it.
// The calls to RawSyscall are okay because they are assembly
// functions that do not grow the stack.
//
// func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
func forkAndExecInChild(argv0 *c.Char, argv, envv **c.Char, chroot, dir *c.Char, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
/* TODO(xsw):
// Set up and fork. This returns immediately in the parent or
// if there's an error.
upid, err, mapPipe, locked := forkAndExecInChild1(argv0, argv, envv, chroot, dir, attr, sys, pipe)
if locked {
runtime_AfterFork()
}
if err != 0 {
return 0, err
}
// parent; return PID
pid = int(upid)
if sys.UidMappings != nil || sys.GidMappings != nil {
Close(mapPipe[0])
var err2 Errno
// uid/gid mappings will be written after fork and unshare(2) for user
// namespaces.
if sys.Unshareflags&CLONE_NEWUSER == 0 {
if err := writeUidGidMappings(pid, sys); err != nil {
err2 = err.(Errno)
}
}
RawSyscall(SYS_WRITE, uintptr(mapPipe[1]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
Close(mapPipe[1])
}
return pid, 0
*/
panic("todo: syscall.forkAndExecInChild")
}
const _LINUX_CAPABILITY_VERSION_3 = 0x20080522
type capHeader struct {
version uint32
pid int32
}
type capData struct {
effective uint32
permitted uint32
inheritable uint32
}
type caps struct {
hdr capHeader
data [2]capData
}
// See CAP_TO_INDEX in linux/capability.h:
func capToIndex(cap uintptr) uintptr { return cap >> 5 }
// See CAP_TO_MASK in linux/capability.h:
func capToMask(cap uintptr) uint32 { return 1 << uint(cap&31) }
// cloneArgs holds arguments for clone3 Linux syscall.
type cloneArgs struct {
flags uint64 // Flags bit mask
pidFD uint64 // Where to store PID file descriptor (int *)
childTID uint64 // Where to store child TID, in child's memory (pid_t *)
parentTID uint64 // Where to store child TID, in parent's memory (pid_t *)
exitSignal uint64 // Signal to deliver to parent on child termination
stack uint64 // Pointer to lowest byte of stack
stackSize uint64 // Size of stack
tls uint64 // Location of new TLS
setTID uint64 // Pointer to a pid_t array (since Linux 5.5)
setTIDSize uint64 // Number of elements in set_tid (since Linux 5.5)
cgroup uint64 // File descriptor for target cgroup of child (since Linux 5.7)
}
// forkAndExecInChild1 implements the body of forkAndExecInChild up to
// the parent's post-fork path. This is a separate function so we can
// separate the child's and parent's stack frames if we're using
// vfork.
//
// This is go:noinline because the point is to keep the stack frames
// of this and forkAndExecInChild separate.
//
//go:noinline
//go:norace
//go:nocheckptr
func forkAndExecInChild1(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid uintptr, err1 Errno, mapPipe [2]int, locked bool) {
/* TODO(xsw):
// Defined in linux/prctl.h starting with Linux 4.3.
const (
PR_CAP_AMBIENT = 0x2f
PR_CAP_AMBIENT_RAISE = 0x2
)
// vfork requires that the child not touch any of the parent's
// active stack frames. Hence, the child does all post-fork
// processing in this stack frame and never returns, while the
// parent returns immediately from this frame and does all
// post-fork processing in the outer frame.
//
// Declare all variables at top in case any
// declarations require heap allocation (e.g., err2).
// ":=" should not be used to declare any variable after
// the call to runtime_BeforeFork.
//
// NOTE(bcmills): The allocation behavior described in the above comment
// seems to lack a corresponding test, and it may be rendered invalid
// by an otherwise-correct change in the compiler.
var (
err2 Errno
nextfd int
i int
caps caps
fd1, flags uintptr
puid, psetgroups, pgid []byte
uidmap, setgroups, gidmap []byte
clone3 *cloneArgs
pgrp int32
dirfd int
cred *Credential
ngroups, groups uintptr
c uintptr
)
rlim, rlimOK := origRlimitNofile.Load().(Rlimit)
if sys.UidMappings != nil {
puid = []byte("/proc/self/uid_map\000")
uidmap = formatIDMappings(sys.UidMappings)
}
if sys.GidMappings != nil {
psetgroups = []byte("/proc/self/setgroups\000")
pgid = []byte("/proc/self/gid_map\000")
if sys.GidMappingsEnableSetgroups {
setgroups = []byte("allow\000")
} else {
setgroups = []byte("deny\000")
}
gidmap = formatIDMappings(sys.GidMappings)
}
// Record parent PID so child can test if it has died.
ppid, _ := rawSyscallNoError(SYS_GETPID, 0, 0, 0)
// Guard against side effects of shuffling fds below.
// Make sure that nextfd is beyond any currently open files so
// that we can't run the risk of overwriting any of them.
fd := make([]int, len(attr.Files))
nextfd = len(attr.Files)
for i, ufd := range attr.Files {
if nextfd < int(ufd) {
nextfd = int(ufd)
}
fd[i] = int(ufd)
}
nextfd++
// Allocate another pipe for parent to child communication for
// synchronizing writing of User ID/Group ID mappings.
if sys.UidMappings != nil || sys.GidMappings != nil {
if err := forkExecPipe(mapPipe[:]); err != nil {
err1 = err.(Errno)
return
}
}
flags = sys.Cloneflags
if sys.Cloneflags&CLONE_NEWUSER == 0 && sys.Unshareflags&CLONE_NEWUSER == 0 {
flags |= CLONE_VFORK | CLONE_VM
}
// Whether to use clone3.
if sys.UseCgroupFD {
clone3 = &cloneArgs{
flags: uint64(flags) | CLONE_INTO_CGROUP,
exitSignal: uint64(SIGCHLD),
cgroup: uint64(sys.CgroupFD),
}
} else if flags&CLONE_NEWTIME != 0 {
clone3 = &cloneArgs{
flags: uint64(flags),
exitSignal: uint64(SIGCHLD),
}
}
// About to call fork.
// No more allocation or calls of non-assembly functions.
runtime_BeforeFork()
locked = true
if clone3 != nil {
pid, err1 = rawVforkSyscall(_SYS_clone3, uintptr(unsafe.Pointer(clone3)), unsafe.Sizeof(*clone3))
} else {
flags |= uintptr(SIGCHLD)
if runtime.GOARCH == "s390x" {
// On Linux/s390, the first two arguments of clone(2) are swapped.
pid, err1 = rawVforkSyscall(SYS_CLONE, 0, flags)
} else {
pid, err1 = rawVforkSyscall(SYS_CLONE, flags, 0)
}
}
if err1 != 0 || pid != 0 {
// If we're in the parent, we must return immediately
// so we're not in the same stack frame as the child.
// This can at most use the return PC, which the child
// will not modify, and the results of
// rawVforkSyscall, which must have been written after
// the child was replaced.
return
}
// Fork succeeded, now in child.
// Enable the "keep capabilities" flag to set ambient capabilities later.
if len(sys.AmbientCaps) > 0 {
_, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_KEEPCAPS, 1, 0, 0, 0, 0)
if err1 != 0 {
goto childerror
}
}
// Wait for User ID/Group ID mappings to be written.
if sys.UidMappings != nil || sys.GidMappings != nil {
if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(mapPipe[1]), 0, 0); err1 != 0 {
goto childerror
}
pid, _, err1 = RawSyscall(SYS_READ, uintptr(mapPipe[0]), uintptr(unsafe.Pointer(&err2)), unsafe.Sizeof(err2))
if err1 != 0 {
goto childerror
}
if pid != unsafe.Sizeof(err2) {
err1 = EINVAL
goto childerror
}
if err2 != 0 {
err1 = err2
goto childerror
}
}
// Session ID
if sys.Setsid {
_, _, err1 = RawSyscall(SYS_SETSID, 0, 0, 0)
if err1 != 0 {
goto childerror
}
}
// Set process group
if sys.Setpgid || sys.Foreground {
// Place child in process group.
_, _, err1 = RawSyscall(SYS_SETPGID, 0, uintptr(sys.Pgid), 0)
if err1 != 0 {
goto childerror
}
}
if sys.Foreground {
pgrp = int32(sys.Pgid)
if pgrp == 0 {
pid, _ = rawSyscallNoError(SYS_GETPID, 0, 0, 0)
pgrp = int32(pid)
}
// Place process group in foreground.
_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgrp)))
if err1 != 0 {
goto childerror
}
}
// Restore the signal mask. We do this after TIOCSPGRP to avoid
// having the kernel send a SIGTTOU signal to the process group.
runtime_AfterForkInChild()
// Unshare
if sys.Unshareflags != 0 {
_, _, err1 = RawSyscall(SYS_UNSHARE, sys.Unshareflags, 0, 0)
if err1 != 0 {
goto childerror
}
if sys.Unshareflags&CLONE_NEWUSER != 0 && sys.GidMappings != nil {
dirfd = int(_AT_FDCWD)
if fd1, _, err1 = RawSyscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(&psetgroups[0])), uintptr(O_WRONLY), 0, 0, 0); err1 != 0 {
goto childerror
}
pid, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&setgroups[0])), uintptr(len(setgroups)))
if err1 != 0 {
goto childerror
}
if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(fd1), 0, 0); err1 != 0 {
goto childerror
}
if fd1, _, err1 = RawSyscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(&pgid[0])), uintptr(O_WRONLY), 0, 0, 0); err1 != 0 {
goto childerror
}
pid, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&gidmap[0])), uintptr(len(gidmap)))
if err1 != 0 {
goto childerror
}
if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(fd1), 0, 0); err1 != 0 {
goto childerror
}
}
if sys.Unshareflags&CLONE_NEWUSER != 0 && sys.UidMappings != nil {
dirfd = int(_AT_FDCWD)
if fd1, _, err1 = RawSyscall6(SYS_OPENAT, uintptr(dirfd), uintptr(unsafe.Pointer(&puid[0])), uintptr(O_WRONLY), 0, 0, 0); err1 != 0 {
goto childerror
}
pid, _, err1 = RawSyscall(SYS_WRITE, uintptr(fd1), uintptr(unsafe.Pointer(&uidmap[0])), uintptr(len(uidmap)))
if err1 != 0 {
goto childerror
}
if _, _, err1 = RawSyscall(SYS_CLOSE, uintptr(fd1), 0, 0); err1 != 0 {
goto childerror
}
}
// The unshare system call in Linux doesn't unshare mount points
// mounted with --shared. Systemd mounts / with --shared. For a
// long discussion of the pros and cons of this see debian bug 739593.
// The Go model of unsharing is more like Plan 9, where you ask
// to unshare and the namespaces are unconditionally unshared.
// To make this model work we must further mark / as MS_PRIVATE.
// This is what the standard unshare command does.
if sys.Unshareflags&CLONE_NEWNS == CLONE_NEWNS {
_, _, err1 = RawSyscall6(SYS_MOUNT, uintptr(unsafe.Pointer(&none[0])), uintptr(unsafe.Pointer(&slash[0])), 0, MS_REC|MS_PRIVATE, 0, 0)
if err1 != 0 {
goto childerror
}
}
}
// Chroot
if chroot != nil {
_, _, err1 = RawSyscall(SYS_CHROOT, uintptr(unsafe.Pointer(chroot)), 0, 0)
if err1 != 0 {
goto childerror
}
}
// User and groups
if cred = sys.Credential; cred != nil {
ngroups = uintptr(len(cred.Groups))
groups = uintptr(0)
if ngroups > 0 {
groups = uintptr(unsafe.Pointer(&cred.Groups[0]))
}
if !(sys.GidMappings != nil && !sys.GidMappingsEnableSetgroups && ngroups == 0) && !cred.NoSetGroups {
_, _, err1 = RawSyscall(_SYS_setgroups, ngroups, groups, 0)
if err1 != 0 {
goto childerror
}
}
_, _, err1 = RawSyscall(sys_SETGID, uintptr(cred.Gid), 0, 0)
if err1 != 0 {
goto childerror
}
_, _, err1 = RawSyscall(sys_SETUID, uintptr(cred.Uid), 0, 0)
if err1 != 0 {
goto childerror
}
}
if len(sys.AmbientCaps) != 0 {
// Ambient capabilities were added in the 4.3 kernel,
// so it is safe to always use _LINUX_CAPABILITY_VERSION_3.
caps.hdr.version = _LINUX_CAPABILITY_VERSION_3
if _, _, err1 = RawSyscall(SYS_CAPGET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
goto childerror
}
for _, c = range sys.AmbientCaps {
// Add the c capability to the permitted and inheritable capability mask,
// otherwise we will not be able to add it to the ambient capability mask.
caps.data[capToIndex(c)].permitted |= capToMask(c)
caps.data[capToIndex(c)].inheritable |= capToMask(c)
}
if _, _, err1 = RawSyscall(SYS_CAPSET, uintptr(unsafe.Pointer(&caps.hdr)), uintptr(unsafe.Pointer(&caps.data[0])), 0); err1 != 0 {
goto childerror
}
for _, c = range sys.AmbientCaps {
_, _, err1 = RawSyscall6(SYS_PRCTL, PR_CAP_AMBIENT, uintptr(PR_CAP_AMBIENT_RAISE), c, 0, 0, 0)
if err1 != 0 {
goto childerror
}
}
}
// Chdir
if dir != nil {
_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
if err1 != 0 {
goto childerror
}
}
// Parent death signal
if sys.Pdeathsig != 0 {
_, _, err1 = RawSyscall6(SYS_PRCTL, PR_SET_PDEATHSIG, uintptr(sys.Pdeathsig), 0, 0, 0, 0)
if err1 != 0 {
goto childerror
}
// Signal self if parent is already dead. This might cause a
// duplicate signal in rare cases, but it won't matter when
// using SIGKILL.
pid, _ = rawSyscallNoError(SYS_GETPPID, 0, 0, 0)
if pid != ppid {
pid, _ = rawSyscallNoError(SYS_GETPID, 0, 0, 0)
_, _, err1 = RawSyscall(SYS_KILL, pid, uintptr(sys.Pdeathsig), 0)
if err1 != 0 {
goto childerror
}
}
}
// Pass 1: look for fd[i] < i and move those up above len(fd)
// so that pass 2 won't stomp on an fd it needs later.
if pipe < nextfd {
_, _, err1 = RawSyscall(SYS_DUP3, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
if err1 != 0 {
goto childerror
}
pipe = nextfd
nextfd++
}
for i = 0; i < len(fd); i++ {
if fd[i] >= 0 && fd[i] < i {
if nextfd == pipe { // don't stomp on pipe
nextfd++
}
_, _, err1 = RawSyscall(SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
if err1 != 0 {
goto childerror
}
fd[i] = nextfd
nextfd++
}
}
// Pass 2: dup fd[i] down onto i.
for i = 0; i < len(fd); i++ {
if fd[i] == -1 {
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
continue
}
if fd[i] == i {
// dup2(i, i) won't clear close-on-exec flag on Linux,
// probably not elsewhere either.
_, _, err1 = RawSyscall(fcntl64Syscall, uintptr(fd[i]), F_SETFD, 0)
if err1 != 0 {
goto childerror
}
continue
}
// The new fd is created NOT close-on-exec,
// which is exactly what we want.
_, _, err1 = RawSyscall(SYS_DUP3, uintptr(fd[i]), uintptr(i), 0)
if err1 != 0 {
goto childerror
}
}
// By convention, we don't close-on-exec the fds we are
// started with, so if len(fd) < 3, close 0, 1, 2 as needed.
// Programs that know they inherit fds >= 3 will need
// to set them close-on-exec.
for i = len(fd); i < 3; i++ {
RawSyscall(SYS_CLOSE, uintptr(i), 0, 0)
}
// Detach fd 0 from tty
if sys.Noctty {
_, _, err1 = RawSyscall(SYS_IOCTL, 0, uintptr(TIOCNOTTY), 0)
if err1 != 0 {
goto childerror
}
}
// Set the controlling TTY to Ctty
if sys.Setctty {
_, _, err1 = RawSyscall(SYS_IOCTL, uintptr(sys.Ctty), uintptr(TIOCSCTTY), 1)
if err1 != 0 {
goto childerror
}
}
// Restore original rlimit.
if rlimOK && rlim.Cur != 0 {
rawSetrlimit(RLIMIT_NOFILE, &rlim)
}
// Enable tracing if requested.
// Do this right before exec so that we don't unnecessarily trace the runtime
// setting up after the fork. See issue #21428.
if sys.Ptrace {
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
if err1 != 0 {
goto childerror
}
}
// Time to exec.
_, _, err1 = RawSyscall(SYS_EXECVE,
uintptr(unsafe.Pointer(argv0)),
uintptr(unsafe.Pointer(&argv[0])),
uintptr(unsafe.Pointer(&envv[0])))
childerror:
// send error code on pipe
RawSyscall(SYS_WRITE, uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
for {
RawSyscall(SYS_EXIT, 253, 0, 0)
}
*/
panic("todo: syscall.forkAndExecInChild1")
}
func formatIDMappings(idMap []SysProcIDMap) []byte {
/* TODO(xsw):
var data []byte
for _, im := range idMap {
data = append(data, itoa.Itoa(im.ContainerID)+" "+itoa.Itoa(im.HostID)+" "+itoa.Itoa(im.Size)+"\n"...)
}
return data
*/
panic("todo: syscall.formatIDMappings")
}
// writeIDMappings writes the user namespace User ID or Group ID mappings to the specified path.
func writeIDMappings(path string, idMap []SysProcIDMap) error {
/* TODO(xsw):
fd, err := Open(path, O_RDWR, 0)
if err != nil {
return err
}
if _, err := Write(fd, formatIDMappings(idMap)); err != nil {
Close(fd)
return err
}
if err := Close(fd); err != nil {
return err
}
return nil
*/
panic("todo: syscall.writeIDMappings")
}
// writeSetgroups writes to /proc/PID/setgroups "deny" if enable is false
// and "allow" if enable is true.
// This is needed since kernel 3.19, because you can't write gid_map without
// disabling setgroups() system call.
func writeSetgroups(pid int, enable bool) error {
/* TODO(xsw):
sgf := "/proc/" + itoa.Itoa(pid) + "/setgroups"
fd, err := Open(sgf, O_RDWR, 0)
if err != nil {
return err
}
var data []byte
if enable {
data = []byte("allow")
} else {
data = []byte("deny")
}
if _, err := Write(fd, data); err != nil {
Close(fd)
return err
}
return Close(fd)
*/
panic("todo: syscall.writeSetgroups")
}
// writeUidGidMappings writes User ID and Group ID mappings for user namespaces
// for a process and it is called from the parent process.
func writeUidGidMappings(pid int, sys *SysProcAttr) error {
/* TODO(xsw):
if sys.UidMappings != nil {
uidf := "/proc/" + itoa.Itoa(pid) + "/uid_map"
if err := writeIDMappings(uidf, sys.UidMappings); err != nil {
return err
}
}
if sys.GidMappings != nil {
// If the kernel is too old to support /proc/PID/setgroups, writeSetGroups will return ENOENT; this is OK.
if err := writeSetgroups(pid, sys.GidMappingsEnableSetgroups); err != nil && err != ENOENT {
return err
}
gidf := "/proc/" + itoa.Itoa(pid) + "/gid_map"
if err := writeIDMappings(gidf, sys.GidMappings); err != nil {
return err
}
}
return nil
*/
panic("todo: syscall.writeUidGidMappings")
}

View File

@@ -0,0 +1,231 @@
// Copyright 2009 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 unix
// Fork, exec, wait, etc.
package syscall
import (
"errors"
"runtime"
"sync"
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
// ForkLock is used to synchronize creation of new file descriptors
// with fork.
//
// We want the child in a fork/exec sequence to inherit only the
// file descriptors we intend. To do that, we mark all file
// descriptors close-on-exec and then, in the child, explicitly
// unmark the ones we want the exec'ed program to keep.
// Unix doesn't make this easy: there is, in general, no way to
// allocate a new file descriptor close-on-exec. Instead you
// have to allocate the descriptor and then mark it close-on-exec.
// If a fork happens between those two events, the child's exec
// will inherit an unwanted file descriptor.
//
// This lock solves that race: the create new fd/mark close-on-exec
// operation is done holding ForkLock for reading, and the fork itself
// is done holding ForkLock for writing. At least, that's the idea.
// There are some complications.
//
// Some system calls that create new file descriptors can block
// for arbitrarily long times: open on a hung NFS server or named
// pipe, accept on a socket, and so on. We can't reasonably grab
// the lock across those operations.
//
// It is worse to inherit some file descriptors than others.
// If a non-malicious child accidentally inherits an open ordinary file,
// that's not a big deal. On the other hand, if a long-lived child
// accidentally inherits the write end of a pipe, then the reader
// of that pipe will not see EOF until that child exits, potentially
// causing the parent program to hang. This is a common problem
// in threaded C programs that use popen.
//
// Luckily, the file descriptors that are most important not to
// inherit are not the ones that can take an arbitrarily long time
// to create: pipe returns instantly, and the net package uses
// non-blocking I/O to accept on a listening socket.
// The rules for which file descriptor-creating operations use the
// ForkLock are as follows:
//
// - Pipe. Use pipe2 if available. Otherwise, does not block,
// so use ForkLock.
// - Socket. Use SOCK_CLOEXEC if available. Otherwise, does not
// block, so use ForkLock.
// - Open. Use O_CLOEXEC if available. Otherwise, may block,
// so live with the race.
// - Dup. Use F_DUPFD_CLOEXEC or dup3 if available. Otherwise,
// does not block, so use ForkLock.
var ForkLock sync.RWMutex
func CloseOnExec(fd int) {
os.Fcntl(c.Int(fd), syscall.F_SETFD, syscall.FD_CLOEXEC)
}
func SetNonblock(fd int, nonblocking bool) (err error) {
/* TODO(xsw):
flag, err := fcntl(fd, F_GETFL, 0)
if err != nil {
return err
}
if nonblocking {
flag |= O_NONBLOCK
} else {
flag &^= O_NONBLOCK
}
_, err = fcntl(fd, F_SETFL, flag)
return err
*/
panic("todo: syscall.SetNonblock")
}
// Credential holds user and group identities to be assumed
// by a child process started by StartProcess.
type Credential struct {
Uid uint32 // User ID.
Gid uint32 // Group ID.
Groups []uint32 // Supplementary group IDs.
NoSetGroups bool // If true, don't set supplementary groups
}
// ProcAttr holds attributes that will be applied to a new process started
// by StartProcess.
type ProcAttr struct {
Dir string // Current working directory.
Env []string // Environment.
Files []uintptr // File descriptors.
Sys *SysProcAttr
}
var zeroProcAttr ProcAttr
var zeroSysProcAttr SysProcAttr
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
var p [2]int
var n int
var err1 Errno
var wstatus WaitStatus
if attr == nil {
attr = &zeroProcAttr
}
sys := attr.Sys
if sys == nil {
sys = &zeroSysProcAttr
}
// Convert args to C form.
argv0p := c.AllocaCStr(argv0)
argvp := c.AllocaCStrs(argv, true)
envvp := c.AllocaCStrs(attr.Env, true)
if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv) > 0 && len(argv[0]) > len(argv0) {
*argvp = argv0p
}
var chroot *c.Char
if sys.Chroot != "" {
chroot = c.AllocaCStr(sys.Chroot)
}
var dir *c.Char
if attr.Dir != "" {
dir = c.AllocaCStr(attr.Dir)
}
// Both Setctty and Foreground use the Ctty field,
// but they give it slightly different meanings.
if sys.Setctty && sys.Foreground {
return 0, errors.New("both Setctty and Foreground set in SysProcAttr")
}
if sys.Setctty && sys.Ctty >= len(attr.Files) {
return 0, errors.New("Setctty set but Ctty not valid in child")
}
acquireForkLock()
// Allocate child status pipe close on exec.
if err = forkExecPipe(p[:]); err != nil {
releaseForkLock()
return 0, err
}
// Kick off child.
pid, err1 = forkAndExecInChild(argv0p, argvp, envvp, chroot, dir, attr, sys, p[1])
if err1 != 0 {
Close(p[0])
Close(p[1])
releaseForkLock()
return 0, Errno(err1)
}
releaseForkLock()
// Read child error status from pipe.
Close(p[1])
for {
n, err = readlen(p[0], (*byte)(unsafe.Pointer(&err1)), int(unsafe.Sizeof(err1)))
if err != Errno(syscall.EINTR) {
break
}
}
Close(p[0])
if err != nil || n != 0 {
if n == int(unsafe.Sizeof(err1)) {
err = Errno(err1)
}
if err == nil {
err = Errno(syscall.EPIPE)
}
// Child failed; wait for it to exit, to make sure
// the zombies don't accumulate.
_, err1 := Wait4(pid, &wstatus, 0, nil)
for err1 == Errno(syscall.EINTR) {
_, err1 = Wait4(pid, &wstatus, 0, nil)
}
return 0, err
}
// Read got EOF, so pipe closed on exec, so exec succeeded.
return pid, nil
}
// Combination of fork and exec, careful to be thread safe.
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
return forkExec(argv0, argv, attr)
}
// StartProcess wraps ForkExec for package os.
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
pid, err = forkExec(argv0, argv, attr)
return pid, 0, err
}
/* TODO(xsw):
// Implemented in runtime package.
func runtime_BeforeExec()
func runtime_AfterExec()
// execveLibc is non-nil on OS using libc syscall, set to execve in exec_libc.go; this
// avoids a build dependency for other platforms.
var execveLibc func(path uintptr, argv uintptr, envp uintptr) Errno
var execveDarwin func(path *byte, argv **byte, envp **byte) error
var execveOpenBSD func(path *byte, argv **byte, envp **byte) error
*/
// Exec invokes the execve(2) system call.
func Exec(argv0 string, argv []string, envv []string) (err error) {
ret := os.Execve(c.AllocaCStr(argv0), c.AllocaCStrs(argv, true), c.AllocaCStrs(envv, true))
if ret == 0 {
return nil
}
return Errno(ret)
}

View File

@@ -0,0 +1,39 @@
// Copyright 2011 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 aix || darwin
package syscall
import (
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
// forkExecPipe opens a pipe and non-atomically sets O_CLOEXEC on both file
// descriptors.
func forkExecPipe(p []int) error {
err := Pipe(p)
if err != nil {
return err
}
ret := os.Fcntl(c.Int(p[0]), syscall.F_SETFD, syscall.FD_CLOEXEC)
if ret != 0 {
return Errno(ret)
}
ret = os.Fcntl(c.Int(p[1]), syscall.F_SETFD, syscall.FD_CLOEXEC)
if ret != 0 {
return Errno(ret)
}
return nil
}
func acquireForkLock() {
ForkLock.Lock()
}
func releaseForkLock() {
ForkLock.Unlock()
}

View File

@@ -0,0 +1,105 @@
// Copyright 2017 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 dragonfly || freebsd || linux || netbsd || openbsd || solaris
package syscall
import (
"sync"
"github.com/goplus/llgo/c/syscall"
)
// forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file
// descriptors.
func forkExecPipe(p []int) error {
return Pipe2(p, syscall.O_CLOEXEC)
}
var (
// Guard the forking variable.
forkingLock sync.Mutex
// Number of goroutines currently forking, and thus the
// number of goroutines holding a conceptual write lock
// on ForkLock.
forking int
)
// TODO(xsw):
// hasWaitingReaders reports whether any goroutine is waiting
// to acquire a read lock on rw. It is defined in the sync package.
func hasWaitingReaders(rw *sync.RWMutex) bool {
panic("todo: syscall.hasWaitingReaders in sync package")
}
// acquireForkLock acquires a write lock on ForkLock.
// ForkLock is exported and we've promised that during a fork
// we will call ForkLock.Lock, so that no other threads create
// new fds that are not yet close-on-exec before we fork.
// But that forces all fork calls to be serialized, which is bad.
// But we haven't promised that serialization, and it is essentially
// undetectable by other users of ForkLock, which is good.
// Avoid the serialization by ensuring that ForkLock is locked
// at the first fork and unlocked when there are no more forks.
func acquireForkLock() {
forkingLock.Lock()
defer forkingLock.Unlock()
if forking == 0 {
// There is no current write lock on ForkLock.
ForkLock.Lock()
forking++
return
}
// ForkLock is currently locked for writing.
if hasWaitingReaders(&ForkLock) {
// ForkLock is locked for writing, and at least one
// goroutine is waiting to read from it.
// To avoid lock starvation, allow readers to proceed.
// The simple way to do this is for us to acquire a
// read lock. That will block us until all current
// conceptual write locks are released.
//
// Note that this case is unusual on modern systems
// with O_CLOEXEC and SOCK_CLOEXEC. On those systems
// the standard library should never take a read
// lock on ForkLock.
forkingLock.Unlock()
ForkLock.RLock()
ForkLock.RUnlock()
forkingLock.Lock()
// Readers got a chance, so now take the write lock.
if forking == 0 {
ForkLock.Lock()
}
}
forking++
}
// releaseForkLock releases the conceptual write lock on ForkLock
// acquired by acquireForkLock.
func releaseForkLock() {
forkingLock.Lock()
defer forkingLock.Unlock()
if forking <= 0 {
panic("syscall.releaseForkLock: negative count")
}
forking--
if forking == 0 {
// No more conceptual write locks.
ForkLock.Unlock()
}
}

View File

@@ -0,0 +1,52 @@
// Copyright 2022 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 unix
package syscall
import (
"sync/atomic"
"github.com/goplus/llgo/c/syscall"
)
// origRlimitNofile, if not {0, 0}, is the original soft RLIMIT_NOFILE.
// When we can assume that we are bootstrapping with Go 1.19,
// this can be atomic.Pointer[Rlimit].
var origRlimitNofile atomic.Value // of Rlimit
// Some systems set an artificially low soft limit on open file count, for compatibility
// with code that uses select and its hard-coded maximum file descriptor
// (limited by the size of fd_set).
//
// Go does not use select, so it should not be subject to these limits.
// On some systems the limit is 256, which is very easy to run into,
// even in simple programs like gofmt when they parallelize walking
// a file tree.
//
// After a long discussion on go.dev/issue/46279, we decided the
// best approach was for Go to raise the limit unconditionally for itself,
// and then leave old software to set the limit back as needed.
// Code that really wants Go to leave the limit alone can set the hard limit,
// which Go of course has no choice but to respect.
func init() {
var lim Rlimit
if err := Getrlimit(syscall.RLIMIT_NOFILE, &lim); err == nil && lim.Cur != lim.Max {
origRlimitNofile.Store(lim)
lim.Cur = lim.Max
adjustFileLimit(&lim)
setrlimit(syscall.RLIMIT_NOFILE, &lim)
}
}
func Setrlimit(resource int, rlim *Rlimit) error {
err := setrlimit(resource, rlim)
if err == nil && resource == syscall.RLIMIT_NOFILE {
// Store zeroes in origRlimitNofile to tell StartProcess
// to not adjust the rlimit in the child process.
origRlimitNofile.Store(Rlimit{0, 0})
}
return err
}

View File

@@ -0,0 +1,20 @@
// Copyright 2022 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 darwin
package syscall
// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go.
func adjustFileLimit(lim *Rlimit) {
// On older macOS, setrlimit(RLIMIT_NOFILE, lim) with lim.Cur = infinity fails.
// Set to the value of kern.maxfilesperproc instead.
n, err := SysctlUint32("kern.maxfilesperproc")
if err != nil {
return
}
if lim.Cur > uint64(n) {
lim.Cur = uint64(n)
}
}

View File

@@ -0,0 +1,10 @@
// Copyright 2022 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 aix || dragonfly || freebsd || linux || netbsd || openbsd || solaris
package syscall
// adjustFileLimit adds per-OS limitations on the Rlimit used for RLIMIT_NOFILE. See rlimit.go.
func adjustFileLimit(lim *Rlimit) {}

View File

@@ -0,0 +1,192 @@
/*
* Copyright (c) 2024 The GoPlus Authors (goplus.org). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package syscall
// llgo:skipall
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
type Timespec syscall.Timespec
type Timeval syscall.Timeval
// Unix returns the time stored in ts as seconds plus nanoseconds.
func (ts *Timespec) Unix() (sec int64, nsec int64) {
return int64(ts.Sec), int64(ts.Nsec)
}
// Unix returns the time stored in tv as seconds plus nanoseconds.
func (tv *Timeval) Unix() (sec int64, nsec int64) {
return int64(tv.Sec), int64(tv.Usec) * 1000
}
// Nano returns the time stored in ts as nanoseconds.
func (ts *Timespec) Nano() int64 {
return int64(ts.Sec)*1e9 + int64(ts.Nsec)
}
// Nano returns the time stored in tv as nanoseconds.
func (tv *Timeval) Nano() int64 {
return int64(tv.Sec)*1e9 + int64(tv.Usec)*1000
}
func Getpagesize() int {
panic("todo: syscall.Getpagesize")
}
func Exit(code int) {
os.Exit(c.Int(code))
}
func Getcwd(buf []byte) (n int, err error) {
ptr := unsafe.Pointer(unsafe.SliceData(buf))
ret := os.Getcwd(ptr, uintptr(len(buf)))
if ret != nil {
return int(c.Strlen(ret)), nil
}
return 0, Errno(os.Errno())
}
func Getwd() (string, error) {
wd := os.Getcwd(c.Alloca(os.PATH_MAX), os.PATH_MAX)
if wd != nil {
return c.GoString(wd), nil
}
return "", Errno(os.Errno())
}
func Getpid() (pid int) {
return int(os.Getpid())
}
func Kill(pid int, signum Signal) (err error) {
ret := os.Kill(os.PidT(pid), c.Int(signum))
if ret == 0 {
return nil
}
return Errno(ret)
}
func fork() (uintptr, Errno) {
ret := os.Fork()
if ret >= 0 {
return uintptr(ret), Errno(0)
}
return 0, Errno(os.Errno())
}
func wait4(pid int, wstatus *c.Int, options int, rusage *syscall.Rusage) (wpid int, err error) {
ret := os.Wait4(os.PidT(pid), wstatus, c.Int(options), rusage)
if ret >= 0 {
return int(ret), nil
}
return 0, Errno(os.Errno())
}
func Open(path string, mode int, perm uint32) (fd int, err error) {
ret := os.Open(c.AllocaCStr(path), c.Int(mode), os.ModeT(perm))
if ret >= 0 {
return int(ret), nil
}
return 0, Errno(os.Errno())
}
func Seek(fd int, offset int64, whence int) (newoffset int64, err error) {
ret := os.Lseek(c.Int(fd), os.OffT(offset), c.Int(whence))
if ret >= 0 {
return int64(ret), nil
}
return -1, Errno(os.Errno())
}
func Read(fd int, p []byte) (n int, err error) {
ret := os.Read(c.Int(fd), unsafe.Pointer(unsafe.SliceData(p)), uintptr(len(p)))
if ret >= 0 {
return ret, nil // TODO(xsw): confirm err == nil (not io.EOF) when ret == 0
}
return 0, Errno(os.Errno())
}
func readlen(fd int, buf *byte, nbuf int) (n int, err error) {
ret := os.Read(c.Int(fd), unsafe.Pointer(buf), uintptr(nbuf))
if ret >= 0 {
return ret, nil // TODO(xsw): confirm err == nil (not io.EOF) when ret == 0
}
return 0, Errno(os.Errno())
}
func Close(fd int) (err error) {
ret := os.Close(c.Int(fd))
if ret == 0 {
return nil
}
return Errno(ret)
}
type Stat_t = syscall.Stat_t
func Lstat(path string, stat *Stat_t) (err error) {
ret := os.Lstat(c.AllocaCStr(path), stat)
if ret == 0 {
return nil
}
return Errno(os.Errno())
}
func Stat(path string, stat *Stat_t) (err error) {
ret := os.Stat(c.AllocaCStr(path), stat)
if ret == 0 {
return nil
}
return Errno(os.Errno())
}
func Pipe(p []int) (err error) {
if len(p) != 2 {
return Errno(syscall.EINVAL)
}
var q [2]c.Int
ret := os.Pipe(&q)
if ret == 0 {
p[0] = int(q[0])
p[1] = int(q[1])
return nil
}
return Errno(ret)
}
type Rlimit syscall.Rlimit
func Getrlimit(which int, lim *Rlimit) (err error) {
ret := os.Getrlimit(c.Int(which), (*syscall.Rlimit)(lim))
if ret == 0 {
return nil
}
return Errno(ret)
}
func setrlimit(which int, lim *Rlimit) (err error) {
ret := os.Setrlimit(c.Int(which), (*syscall.Rlimit)(lim))
if ret == 0 {
return nil
}
return Errno(ret)
}

View File

@@ -0,0 +1,525 @@
// Copyright 2009 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 darwin || dragonfly || freebsd || netbsd || openbsd
// BSD system call wrappers shared by *BSD based systems
// including OS X (Darwin) and FreeBSD. Like the other
// syscall_*.go files it is compiled as Go code but also
// used as input to mksyscall which parses the //sys
// lines and generates system call stubs.
package syscall
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
func Getgroups() (gids []int, err error) {
/* TODO(xsw):
n, err := getgroups(0, nil)
if err != nil {
return nil, err
}
if n == 0 {
return nil, nil
}
// Sanity check group count. Max is 16 on BSD.
if n < 0 || n > 1000 {
return nil, EINVAL
}
a := make([]_Gid_t, n)
n, err = getgroups(n, &a[0])
if err != nil {
return nil, err
}
gids = make([]int, n)
for i, v := range a[0:n] {
gids[i] = int(v)
}
return
*/
panic("todo: syscall.Getgroups")
}
func Setgroups(gids []int) (err error) {
/* TODO(xsw):
if len(gids) == 0 {
return setgroups(0, nil)
}
a := make([]_Gid_t, len(gids))
for i, v := range gids {
a[i] = _Gid_t(v)
}
return setgroups(len(a), &a[0])
*/
panic("todo: syscall.Setgroups")
}
func ReadDirent(fd int, buf []byte) (n int, err error) {
/* TODO(xsw):
// Final argument is (basep *uintptr) and the syscall doesn't take nil.
// 64 bits should be enough. (32 bits isn't even on 386). Since the
// actual system call is getdirentries64, 64 is a good guess.
// TODO(rsc): Can we use a single global basep for all calls?
var base = (*uintptr)(unsafe.Pointer(new(uint64)))
return Getdirentries(fd, buf, base)
*/
panic("todo: syscall.ReadDirent")
}
// Wait status is 7 bits at bottom, either 0 (exited),
// 0x7F (stopped), or a signal number that caused an exit.
// The 0x80 bit is whether there was a core dump.
// An extra number (exit code, signal causing a stop)
// is in the high bits.
type WaitStatus uint32
const (
mask = 0x7F
core = 0x80
shift = 8
exited = 0
stopped = 0x7F
)
func (w WaitStatus) Exited() bool { return w&mask == exited }
func (w WaitStatus) ExitStatus() int {
if w&mask != exited {
return -1
}
return int(w >> shift)
}
func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != 0 }
func (w WaitStatus) Signal() Signal {
sig := Signal(w & mask)
if sig == stopped || sig == 0 {
return -1
}
return sig
}
func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }
func (w WaitStatus) Stopped() bool {
return w&mask == stopped && Signal(w>>shift) != Signal(syscall.SIGSTOP)
}
func (w WaitStatus) Continued() bool {
return w&mask == stopped && Signal(w>>shift) == Signal(syscall.SIGSTOP)
}
func (w WaitStatus) StopSignal() Signal {
if !w.Stopped() {
return -1
}
return Signal(w>>shift) & 0xFF
}
func (w WaitStatus) TrapCause() int { return -1 }
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) {
var status c.Int
wpid, err = wait4(pid, &status, options, rusage)
if wstatus != nil {
*wstatus = WaitStatus(status)
}
return
}
/* TODO(xsw):
func (sa *SockaddrInet4) sockaddr() (unsafe.Pointer, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return nil, 0, EINVAL
}
sa.raw.Len = SizeofSockaddrInet4
sa.raw.Family = AF_INET
p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
p[0] = byte(sa.Port >> 8)
p[1] = byte(sa.Port)
sa.raw.Addr = sa.Addr
return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil
}
func (sa *SockaddrInet6) sockaddr() (unsafe.Pointer, _Socklen, error) {
if sa.Port < 0 || sa.Port > 0xFFFF {
return nil, 0, EINVAL
}
sa.raw.Len = SizeofSockaddrInet6
sa.raw.Family = AF_INET6
p := (*[2]byte)(unsafe.Pointer(&sa.raw.Port))
p[0] = byte(sa.Port >> 8)
p[1] = byte(sa.Port)
sa.raw.Scope_id = sa.ZoneId
sa.raw.Addr = sa.Addr
return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil
}
func (sa *SockaddrUnix) sockaddr() (unsafe.Pointer, _Socklen, error) {
name := sa.Name
n := len(name)
if n >= len(sa.raw.Path) || n == 0 {
return nil, 0, EINVAL
}
sa.raw.Len = byte(3 + n) // 2 for Family, Len; 1 for NUL
sa.raw.Family = AF_UNIX
for i := 0; i < n; i++ {
sa.raw.Path[i] = int8(name[i])
}
return unsafe.Pointer(&sa.raw), _Socklen(sa.raw.Len), nil
}
func (sa *SockaddrDatalink) sockaddr() (unsafe.Pointer, _Socklen, error) {
if sa.Index == 0 {
return nil, 0, EINVAL
}
sa.raw.Len = sa.Len
sa.raw.Family = AF_LINK
sa.raw.Index = sa.Index
sa.raw.Type = sa.Type
sa.raw.Nlen = sa.Nlen
sa.raw.Alen = sa.Alen
sa.raw.Slen = sa.Slen
sa.raw.Data = sa.Data
return unsafe.Pointer(&sa.raw), SizeofSockaddrDatalink, nil
}
func anyToSockaddr(rsa *RawSockaddrAny) (Sockaddr, error) {
switch rsa.Addr.Family {
case AF_LINK:
pp := (*RawSockaddrDatalink)(unsafe.Pointer(rsa))
sa := new(SockaddrDatalink)
sa.Len = pp.Len
sa.Family = pp.Family
sa.Index = pp.Index
sa.Type = pp.Type
sa.Nlen = pp.Nlen
sa.Alen = pp.Alen
sa.Slen = pp.Slen
sa.Data = pp.Data
return sa, nil
case AF_UNIX:
pp := (*RawSockaddrUnix)(unsafe.Pointer(rsa))
if pp.Len < 2 || pp.Len > SizeofSockaddrUnix {
return nil, EINVAL
}
sa := new(SockaddrUnix)
// Some BSDs include the trailing NUL in the length, whereas
// others do not. Work around this by subtracting the leading
// family and len. The path is then scanned to see if a NUL
// terminator still exists within the length.
n := int(pp.Len) - 2 // subtract leading Family, Len
for i := 0; i < n; i++ {
if pp.Path[i] == 0 {
// found early NUL; assume Len included the NUL
// or was overestimating.
n = i
break
}
}
sa.Name = string(unsafe.Slice((*byte)(unsafe.Pointer(&pp.Path[0])), n))
return sa, nil
case AF_INET:
pp := (*RawSockaddrInet4)(unsafe.Pointer(rsa))
sa := new(SockaddrInet4)
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
sa.Port = int(p[0])<<8 + int(p[1])
sa.Addr = pp.Addr
return sa, nil
case AF_INET6:
pp := (*RawSockaddrInet6)(unsafe.Pointer(rsa))
sa := new(SockaddrInet6)
p := (*[2]byte)(unsafe.Pointer(&pp.Port))
sa.Port = int(p[0])<<8 + int(p[1])
sa.ZoneId = pp.Scope_id
sa.Addr = pp.Addr
return sa, nil
}
return nil, EAFNOSUPPORT
}
func Accept(fd int) (nfd int, sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len _Socklen = SizeofSockaddrAny
nfd, err = accept(fd, &rsa, &len)
if err != nil {
return
}
if (runtime.GOOS == "darwin" || runtime.GOOS == "ios") && len == 0 {
// Accepted socket has no address.
// This is likely due to a bug in xnu kernels,
// where instead of ECONNABORTED error socket
// is accepted, but has no address.
Close(nfd)
return 0, nil, ECONNABORTED
}
sa, err = anyToSockaddr(&rsa)
if err != nil {
Close(nfd)
nfd = 0
}
return
}
func Getsockname(fd int) (sa Sockaddr, err error) {
var rsa RawSockaddrAny
var len _Socklen = SizeofSockaddrAny
if err = getsockname(fd, &rsa, &len); err != nil {
return
}
// TODO(jsing): DragonFly has a "bug" (see issue 3349), which should be
// reported upstream.
if runtime.GOOS == "dragonfly" && rsa.Addr.Family == AF_UNSPEC && rsa.Addr.Len == 0 {
rsa.Addr.Family = AF_UNIX
rsa.Addr.Len = SizeofSockaddrUnix
}
return anyToSockaddr(&rsa)
}
//sysnb socketpair(domain int, typ int, proto int, fd *[2]int32) (err error)
func GetsockoptByte(fd, level, opt int) (value byte, err error) {
var n byte
vallen := _Socklen(1)
err = getsockopt(fd, level, opt, unsafe.Pointer(&n), &vallen)
return n, err
}
func GetsockoptInet4Addr(fd, level, opt int) (value [4]byte, err error) {
vallen := _Socklen(4)
err = getsockopt(fd, level, opt, unsafe.Pointer(&value[0]), &vallen)
return value, err
}
func GetsockoptIPMreq(fd, level, opt int) (*IPMreq, error) {
var value IPMreq
vallen := _Socklen(SizeofIPMreq)
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen)
return &value, err
}
func GetsockoptIPv6Mreq(fd, level, opt int) (*IPv6Mreq, error) {
var value IPv6Mreq
vallen := _Socklen(SizeofIPv6Mreq)
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen)
return &value, err
}
func GetsockoptIPv6MTUInfo(fd, level, opt int) (*IPv6MTUInfo, error) {
var value IPv6MTUInfo
vallen := _Socklen(SizeofIPv6MTUInfo)
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen)
return &value, err
}
func GetsockoptICMPv6Filter(fd, level, opt int) (*ICMPv6Filter, error) {
var value ICMPv6Filter
vallen := _Socklen(SizeofICMPv6Filter)
err := getsockopt(fd, level, opt, unsafe.Pointer(&value), &vallen)
return &value, err
}
//sys recvfrom(fd int, p []byte, flags int, from *RawSockaddrAny, fromlen *_Socklen) (n int, err error)
//sys sendto(s int, buf []byte, flags int, to unsafe.Pointer, addrlen _Socklen) (err error)
//sys recvmsg(s int, msg *Msghdr, flags int) (n int, err error)
func recvmsgRaw(fd int, p, oob []byte, flags int, rsa *RawSockaddrAny) (n, oobn int, recvflags int, err error) {
var msg Msghdr
msg.Name = (*byte)(unsafe.Pointer(rsa))
msg.Namelen = uint32(SizeofSockaddrAny)
var iov Iovec
if len(p) > 0 {
iov.Base = &p[0]
iov.SetLen(len(p))
}
var dummy byte
if len(oob) > 0 {
// receive at least one normal byte
if len(p) == 0 {
iov.Base = &dummy
iov.SetLen(1)
}
msg.Control = &oob[0]
msg.SetControllen(len(oob))
}
msg.Iov = &iov
msg.Iovlen = 1
if n, err = recvmsg(fd, &msg, flags); err != nil {
return
}
oobn = int(msg.Controllen)
recvflags = int(msg.Flags)
return
}
//sys sendmsg(s int, msg *Msghdr, flags int) (n int, err error)
func sendmsgN(fd int, p, oob []byte, ptr unsafe.Pointer, salen _Socklen, flags int) (n int, err error) {
var msg Msghdr
msg.Name = (*byte)(ptr)
msg.Namelen = uint32(salen)
var iov Iovec
if len(p) > 0 {
iov.Base = &p[0]
iov.SetLen(len(p))
}
var dummy byte
if len(oob) > 0 {
// send at least one normal byte
if len(p) == 0 {
iov.Base = &dummy
iov.SetLen(1)
}
msg.Control = &oob[0]
msg.SetControllen(len(oob))
}
msg.Iov = &iov
msg.Iovlen = 1
if n, err = sendmsg(fd, &msg, flags); err != nil {
return 0, err
}
if len(oob) > 0 && len(p) == 0 {
n = 0
}
return n, nil
}
//sys kevent(kq int, change unsafe.Pointer, nchange int, event unsafe.Pointer, nevent int, timeout *Timespec) (n int, err error)
func Kevent(kq int, changes, events []Kevent_t, timeout *Timespec) (n int, err error) {
var change, event unsafe.Pointer
if len(changes) > 0 {
change = unsafe.Pointer(&changes[0])
}
if len(events) > 0 {
event = unsafe.Pointer(&events[0])
}
return kevent(kq, change, len(changes), event, len(events), timeout)
}
func Sysctl(name string) (value string, err error) {
// Translate name to mib number.
mib, err := nametomib(name)
if err != nil {
return "", err
}
// Find size.
n := uintptr(0)
if err = sysctl(mib, nil, &n, nil, 0); err != nil {
return "", err
}
if n == 0 {
return "", nil
}
// Read into buffer of that size.
buf := make([]byte, n)
if err = sysctl(mib, &buf[0], &n, nil, 0); err != nil {
return "", err
}
// Throw away terminating NUL.
if n > 0 && buf[n-1] == '\x00' {
n--
}
return string(buf[0:n]), nil
}
*/
func SysctlUint32(name string) (value uint32, err error) {
/* TODO(xsw):
// Translate name to mib number.
mib, err := nametomib(name)
if err != nil {
return 0, err
}
// Read into buffer of that size.
n := uintptr(4)
buf := make([]byte, 4)
if err = sysctl(mib, &buf[0], &n, nil, 0); err != nil {
return 0, err
}
if n != 4 {
return 0, Errno(syscall.EIO)
}
return *(*uint32)(unsafe.Pointer(&buf[0])), nil
*/
n := uintptr(4)
ret := os.Sysctlbyname(c.AllocaCStr(name), unsafe.Pointer(&value), &n, nil, 0)
if ret != 0 {
err = Errno(os.Errno())
}
return
}
/*
//sys utimes(path string, timeval *[2]Timeval) (err error)
func Utimes(path string, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
}
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
func UtimesNano(path string, ts []Timespec) error {
if len(ts) != 2 {
return EINVAL
}
err := utimensat(_AT_FDCWD, path, (*[2]Timespec)(unsafe.Pointer(&ts[0])), 0)
if err != ENOSYS {
return err
}
// Not as efficient as it could be because Timespec and
// Timeval have different types in the different OSes
tv := [2]Timeval{
NsecToTimeval(TimespecToNsec(ts[0])),
NsecToTimeval(TimespecToNsec(ts[1])),
}
return utimes(path, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
//sys futimes(fd int, timeval *[2]Timeval) (err error)
func Futimes(fd int, tv []Timeval) (err error) {
if len(tv) != 2 {
return EINVAL
}
return futimes(fd, (*[2]Timeval)(unsafe.Pointer(&tv[0])))
}
//sys fcntl(fd int, cmd int, arg int) (val int, err error)
var mapper = &mmapper{
active: make(map[*byte][]byte),
mmap: mmap,
munmap: munmap,
}
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
return mapper.Mmap(fd, offset, length, prot, flags)
}
func Munmap(b []byte) (err error) {
return mapper.Munmap(b)
}
*/

View File

@@ -0,0 +1,128 @@
// Copyright 2009 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.
// Linux system calls.
// This file is compiled as ordinary Go code,
// but it is also input to mksyscall,
// which parses the //sys lines and generates system call stubs.
// Note that sometimes we use a lowercase //sys name and
// wrap it in our own nicer implementation.
package syscall
import (
_ "unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/os"
"github.com/goplus/llgo/c/syscall"
)
// -----------------------------------------------------------------------------
type WaitStatus uint32
// Wait status is 7 bits at bottom, either 0 (exited),
// 0x7F (stopped), or a signal number that caused an exit.
// The 0x80 bit is whether there was a core dump.
// An extra number (exit code, signal causing a stop)
// is in the high bits. At least that's the idea.
// There are various irregularities. For example, the
// "continued" status is 0xFFFF, distinguishing itself
// from stopped via the core dump bit.
const (
mask = 0x7F
core = 0x80
exited = 0x00
stopped = 0x7F
shift = 8
)
func (w WaitStatus) Exited() bool { return w&mask == exited }
func (w WaitStatus) Signaled() bool { return w&mask != stopped && w&mask != exited }
func (w WaitStatus) Stopped() bool { return w&0xFF == stopped }
func (w WaitStatus) Continued() bool { return w == 0xFFFF }
func (w WaitStatus) CoreDump() bool { return w.Signaled() && w&core != 0 }
func (w WaitStatus) ExitStatus() int {
if !w.Exited() {
return -1
}
return int(w>>shift) & 0xFF
}
func (w WaitStatus) Signal() Signal {
if !w.Signaled() {
return -1
}
return Signal(w & mask)
}
func (w WaitStatus) StopSignal() Signal {
if !w.Stopped() {
return -1
}
return Signal(w>>shift) & 0xFF
}
func (w WaitStatus) TrapCause() int {
/*
if w.StopSignal() != SIGTRAP {
return -1
}
return int(w>>shift) >> 8
*/
panic("todo: syscall.WaitStatus.TrapCause")
}
func Wait4(pid int, wstatus *WaitStatus, options int, rusage *syscall.Rusage) (wpid int, err error) {
var status c.Int
wpid, err = wait4(pid, &status, options, rusage)
if wstatus != nil {
*wstatus = WaitStatus(status)
}
return
}
// -----------------------------------------------------------------------------
// int pipe2(int pipefd[2], int flags);
//
//go:linkname pipe2 C.pipe2
func pipe2(pipefd *[2]c.Int, flags c.Int) c.Int
func Pipe2(p []int, flags int) error {
if len(p) != 2 {
return Errno(syscall.EINVAL)
}
var pp [2]c.Int
ret := pipe2(&pp, c.Int(flags))
if ret == 0 {
p[0] = int(pp[0])
p[1] = int(pp[1])
return nil
}
return Errno(ret)
}
// -----------------------------------------------------------------------------
func Faccessat(dirfd int, path string, mode uint32, flags int) (err error) {
ret := faccessat(c.Int(dirfd), c.AllocaCStr(path), c.Int(mode), c.Int(flags))
if ret != 0 {
return Errno(os.Errno())
}
return nil
}
//go:linkname faccessat C.faccessat
func faccessat(dirfd c.Int, path *c.Char, mode c.Int, flags c.Int) c.Int
// -----------------------------------------------------------------------------

View File

@@ -0,0 +1,71 @@
// Copyright 2009 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 unix
package syscall
import (
"unsafe"
"github.com/goplus/llgo/c"
"github.com/goplus/llgo/c/syscall"
"github.com/goplus/llgo/compiler/internal/lib/internal/oserror"
)
var (
Stdin = 0
Stdout = 1
Stderr = 2
)
type Errno uintptr
func (e Errno) Error() string {
ret := c.Strerror(c.Int(e))
return unsafe.String((*byte)(unsafe.Pointer(ret)), c.Strlen(ret))
}
func (e Errno) Is(target error) bool {
switch target {
case oserror.ErrPermission:
return e == Errno(syscall.EACCES) || e == Errno(syscall.EPERM)
case oserror.ErrExist:
return e == Errno(syscall.EEXIST) || e == Errno(syscall.ENOTEMPTY)
case oserror.ErrNotExist:
return e == Errno(syscall.ENOENT)
// TODO(xsw): go1.21
// case errors.ErrUnsupported:
// return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP
}
return false
}
func (e Errno) Temporary() bool {
return e == Errno(syscall.EINTR) || e == Errno(syscall.EMFILE) ||
e == Errno(syscall.ENFILE) || e.Timeout()
}
func (e Errno) Timeout() bool {
return e == Errno(syscall.EAGAIN) || e == Errno(syscall.EWOULDBLOCK) || e == Errno(syscall.ETIMEDOUT)
}
// A Signal is a number describing a process signal.
// It implements the os.Signal interface.
type Signal int
func (s Signal) Signal() {}
func (s Signal) String() string {
/*
if 0 <= s && int(s) < len(signals) {
str := signals[s]
if str != "" {
return str
}
}
return "signal " + itoa.Itoa(int(s))
*/
panic("todo: syscall.Signal.String")
}