Initial commit: Go 1.23 release state
This commit is contained in:
47
src/syscall/asm9_unix2_amd64.s
Normal file
47
src/syscall/asm9_unix2_amd64.s
Normal file
@@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
//go:build dragonfly || freebsd || netbsd
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// Syscall9 support for AMD64, DragonFly, FreeBSD and NetBSD
|
||||
//
|
||||
|
||||
// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$32-104
|
||||
NO_LOCAL_POINTERS
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ num+0(FP), AX // syscall entry
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), R10
|
||||
MOVQ a5+40(FP), R8
|
||||
MOVQ a6+48(FP), R9
|
||||
MOVQ a7+56(FP), R11
|
||||
MOVQ a8+64(FP), R12
|
||||
MOVQ a9+72(FP), R13
|
||||
|
||||
// only the first 6 arguments can be passed in registers,
|
||||
// the last three should be placed at the top of the stack.
|
||||
MOVQ R11, 8(SP) // arg 7
|
||||
MOVQ R12, 16(SP) // arg 8
|
||||
MOVQ R13, 24(SP) // arg 9
|
||||
|
||||
SYSCALL
|
||||
JCC ok9
|
||||
MOVQ $-1, r1+80(FP) // r1
|
||||
MOVQ $0, r2+88(FP) // r2
|
||||
MOVQ AX, err+96(FP) // errno
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok9:
|
||||
MOVQ AX, r1+80(FP) // r1
|
||||
MOVQ DX, r2+88(FP) // r2
|
||||
MOVQ $0, err+96(FP) // errno
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
21
src/syscall/asm_aix_ppc64.s
Normal file
21
src/syscall/asm_aix_ppc64.s
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for aix/ppc64 are implemented in ../runtime/syscall_aix.go
|
||||
//
|
||||
|
||||
TEXT ·syscall6(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_syscall6(SB)
|
||||
|
||||
TEXT ·rawSyscall6(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_rawSyscall6(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_RawSyscall(SB)
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_Syscall(SB)
|
||||
134
src/syscall/asm_darwin_amd64.s
Normal file
134
src/syscall/asm_darwin_amd64.s
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, Darwin
|
||||
//
|
||||
|
||||
// Trap # in AX, args in DI SI DX, return in AX DX
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno);
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
ADDQ $0x2000000, AX
|
||||
SYSCALL
|
||||
JCC ok
|
||||
MOVQ $-1, r1+32(FP)
|
||||
MOVQ $0, r2+40(FP)
|
||||
MOVQ AX, err+48(FP)
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
MOVQ $0, err+48(FP)
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno);
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), R10
|
||||
MOVQ a5+40(FP), R8
|
||||
MOVQ a6+48(FP), R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
ADDQ $0x2000000, AX
|
||||
SYSCALL
|
||||
JCC ok6
|
||||
MOVQ $-1, r1+56(FP)
|
||||
MOVQ $0, r2+64(FP)
|
||||
MOVQ AX, err+72(FP)
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok6:
|
||||
MOVQ AX, r1+56(FP)
|
||||
MOVQ DX, r2+64(FP)
|
||||
MOVQ $0, err+72(FP)
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), R10
|
||||
MOVQ a5+40(FP), R8
|
||||
MOVQ a6+48(FP), R9
|
||||
MOVQ a7+56(FP), R11
|
||||
MOVQ a8+64(FP), R12
|
||||
MOVQ a9+72(FP), R13
|
||||
SUBQ $32, SP
|
||||
MOVQ R11, 8(SP)
|
||||
MOVQ R12, 16(SP)
|
||||
MOVQ R13, 24(SP)
|
||||
ADDQ $0x2000000, AX
|
||||
SYSCALL
|
||||
JCC ok9
|
||||
ADDQ $32, SP
|
||||
MOVQ $-1, r1+80(FP)
|
||||
MOVQ $0, r2+88(FP)
|
||||
MOVQ AX, err+96(FP)
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok9:
|
||||
ADDQ $32, SP
|
||||
MOVQ AX, r1+80(FP)
|
||||
MOVQ DX, r2+88(FP)
|
||||
MOVQ $0, err+96(FP)
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
ADDQ $0x2000000, AX
|
||||
SYSCALL
|
||||
JCC ok1
|
||||
MOVQ $-1, r1+32(FP)
|
||||
MOVQ $0, r2+40(FP)
|
||||
MOVQ AX, err+48(FP)
|
||||
RET
|
||||
ok1:
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
MOVQ $0, err+48(FP)
|
||||
RET
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), R10
|
||||
MOVQ a5+40(FP), R8
|
||||
MOVQ a6+48(FP), R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
ADDQ $0x2000000, AX
|
||||
SYSCALL
|
||||
JCC ok2
|
||||
MOVQ $-1, r1+56(FP)
|
||||
MOVQ $0, r2+64(FP)
|
||||
MOVQ AX, err+72(FP)
|
||||
RET
|
||||
ok2:
|
||||
MOVQ AX, r1+56(FP)
|
||||
MOVQ DX, r2+64(FP)
|
||||
MOVQ $0, err+72(FP)
|
||||
RET
|
||||
127
src/syscall/asm_darwin_arm64.s
Normal file
127
src/syscall/asm_darwin_arm64.s
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM64, Darwin
|
||||
//
|
||||
|
||||
// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD trap+0(FP), R16
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
SVC $0x80
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+32(FP) // r1
|
||||
MOVD ZR, r2+40(FP) // r2
|
||||
MOVD R0, err+48(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD R1, r2+40(FP) // r2
|
||||
MOVD ZR, err+48(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVD trap+0(FP), R16 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
SVC $0x80
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+32(FP) // r1
|
||||
MOVD ZR, r2+40(FP) // r2
|
||||
MOVD R0, err+48(FP) // err
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD R1, r2+40(FP) // r2
|
||||
MOVD ZR, err+48(FP) // err
|
||||
RET
|
||||
|
||||
// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD trap+0(FP), R16 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
SVC $0x80
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+56(FP) // r1
|
||||
MOVD ZR, r2+64(FP) // r2
|
||||
MOVD R0, err+72(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+56(FP) // r1
|
||||
MOVD R1, r2+64(FP) // r2
|
||||
MOVD ZR, err+72(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVD trap+0(FP), R16 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
SVC $0x80
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+56(FP) // r1
|
||||
MOVD ZR, r2+64(FP) // r2
|
||||
MOVD R0, err+72(FP) // err
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+56(FP) // r1
|
||||
MOVD R1, r2+64(FP) // r2
|
||||
MOVD ZR, R0
|
||||
MOVD R0, err+72(FP) // err
|
||||
RET
|
||||
|
||||
// Actually Syscall7
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD num+0(FP), R16 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
MOVD a7+56(FP), R6
|
||||
//MOVD a8+64(FP), R7
|
||||
//MOVD a9+72(FP), R8
|
||||
SVC $0x80
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+80(FP) // r1
|
||||
MOVD ZR, r2+88(FP) // r2
|
||||
MOVD R0, err+96(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+80(FP) // r1
|
||||
MOVD R1, r2+88(FP) // r2
|
||||
MOVD ZR, err+96(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
130
src/syscall/asm_freebsd_arm.s
Normal file
130
src/syscall/asm_freebsd_arm.s
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, FreeBSD
|
||||
//
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, errno uintptr);
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr);
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, errno uintptr)
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW trap+0(FP), R7 // syscall number
|
||||
MOVW a1+4(FP), R0 // a1
|
||||
MOVW a2+8(FP), R1 // a2
|
||||
MOVW a3+12(FP), R2 // a3
|
||||
SWI $0 // syscall
|
||||
MOVW $0, R2
|
||||
BCS error
|
||||
MOVW R0, r1+16(FP) // r1
|
||||
MOVW R1, r2+20(FP) // r2
|
||||
MOVW R2, err+24(FP) // errno
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
error:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+16(FP) // r1
|
||||
MOVW R2, r2+20(FP) // r2
|
||||
MOVW R0, err+24(FP) // errno
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW trap+0(FP), R7 // syscall number
|
||||
MOVW a1+4(FP), R0 // a1
|
||||
MOVW a2+8(FP), R1 // a2
|
||||
MOVW a3+12(FP), R2 // a3
|
||||
MOVW a4+16(FP), R3 // a4
|
||||
MOVW R13, R4
|
||||
MOVW $a5+20(FP), R13 // a5 to a6 are passed on stack
|
||||
SWI $0 // syscall
|
||||
MOVW R4, R13
|
||||
MOVW $0, R2
|
||||
BCS error6
|
||||
MOVW R0, r1+28(FP) // r1
|
||||
MOVW R1, r2+32(FP) // r2
|
||||
MOVW R2, err+36(FP) // errno
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
error6:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+28(FP) // r1
|
||||
MOVW R2, r2+32(FP) // r2
|
||||
MOVW R0, err+36(FP) // errno
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW num+0(FP), R7 // syscall number
|
||||
MOVW a1+4(FP), R0 // a1
|
||||
MOVW a2+8(FP), R1 // a2
|
||||
MOVW a3+12(FP), R2 // a3
|
||||
MOVW a4+16(FP), R3 // a4
|
||||
MOVW R13, R4
|
||||
MOVW $a5+20(FP), R13 // a5 to a9 are passed on stack
|
||||
SWI $0 // syscall
|
||||
MOVW R4, R13
|
||||
MOVW $0, R2
|
||||
BCS error9
|
||||
MOVW R0, r1+40(FP) // r1
|
||||
MOVW R1, r2+44(FP) // r2
|
||||
MOVW R2, err+48(FP) // errno
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
error9:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+40(FP) // r1
|
||||
MOVW R2, r2+44(FP) // r2
|
||||
MOVW R0, err+48(FP) // errno
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
MOVW trap+0(FP), R7 // syscall number
|
||||
MOVW a1+4(FP), R0 // a1
|
||||
MOVW a2+8(FP), R1 // a2
|
||||
MOVW a3+12(FP), R2 // a3
|
||||
SWI $0 // syscall
|
||||
MOVW $0, R2
|
||||
BCS errorr
|
||||
MOVW R0, r1+16(FP) // r1
|
||||
MOVW R1, r2+20(FP) // r2
|
||||
MOVW R2, err+24(FP) // errno
|
||||
RET
|
||||
errorr:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+16(FP) // r1
|
||||
MOVW R2, r2+20(FP) // r2
|
||||
MOVW R0, err+24(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
MOVW trap+0(FP), R7 // syscall number
|
||||
MOVW a1+4(FP), R0 // a1
|
||||
MOVW a2+8(FP), R1 // a2
|
||||
MOVW a3+12(FP), R2 // a3
|
||||
MOVW a4+16(FP), R3 // a4
|
||||
MOVW R13, R4
|
||||
MOVW $a5+20(FP), R13 // a5 to a6 are passed on stack
|
||||
SWI $0 // syscall
|
||||
MOVW R4, R13
|
||||
MOVW $0, R2
|
||||
BCS errorr6
|
||||
MOVW R0, r1+28(FP) // r1
|
||||
MOVW R1, r2+32(FP) // r2
|
||||
MOVW R2, err+36(FP) // errno
|
||||
RET
|
||||
errorr6:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+28(FP) // r1
|
||||
MOVW R2, r2+32(FP) // r2
|
||||
MOVW R0, err+36(FP) // errno
|
||||
RET
|
||||
128
src/syscall/asm_freebsd_arm64.s
Normal file
128
src/syscall/asm_freebsd_arm64.s
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM64, FreeBSD
|
||||
//
|
||||
|
||||
#define SYS_syscall 0
|
||||
|
||||
// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+32(FP)
|
||||
MOVD ZR, r2+40(FP)
|
||||
MOVD R0, err+48(FP)
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP)
|
||||
MOVD R1, r2+40(FP)
|
||||
MOVD ZR, err+48(FP)
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+32(FP)
|
||||
MOVD ZR, r2+40(FP)
|
||||
MOVD R0, err+48(FP)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP)
|
||||
MOVD R1, r2+40(FP)
|
||||
MOVD ZR, err+48(FP)
|
||||
RET
|
||||
|
||||
// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+56(FP)
|
||||
MOVD ZR, r2+64(FP)
|
||||
MOVD R0, err+72(FP)
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+56(FP)
|
||||
MOVD R1, r2+64(FP)
|
||||
MOVD ZR, err+72(FP)
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+56(FP)
|
||||
MOVD ZR, r2+64(FP)
|
||||
MOVD R0, err+72(FP)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+56(FP)
|
||||
MOVD R1, r2+64(FP)
|
||||
MOVD ZR, err+72(FP)
|
||||
RET
|
||||
|
||||
// Actually Syscall7
|
||||
// func Syscall9(num uintptr, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD num+0(FP), R8 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
MOVD a7+56(FP), R6
|
||||
// MOVD a8+64(FP), R7
|
||||
// MOVD a9+72(FP), R8
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+80(FP)
|
||||
MOVD ZR, r2+88(FP)
|
||||
MOVD R0, err+96(FP)
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+80(FP)
|
||||
MOVD R1, r2+88(FP)
|
||||
MOVD ZR, err+96(FP)
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
125
src/syscall/asm_freebsd_riscv64.s
Normal file
125
src/syscall/asm_freebsd_riscv64.s
Normal file
@@ -0,0 +1,125 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for riscv64, FreeBSD
|
||||
//
|
||||
|
||||
// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV trap+0(FP), T0 // syscall entry
|
||||
ECALL
|
||||
BNE T0, ZERO, err
|
||||
MOV A0, r1+32(FP) // r1
|
||||
MOV A1, r2+40(FP) // r2
|
||||
MOV ZERO, err+48(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
err:
|
||||
MOV $-1, T0
|
||||
MOV T0, r1+32(FP) // r1
|
||||
MOV ZERO, r2+40(FP) // r2
|
||||
MOV A0, err+48(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV a4+32(FP), A3
|
||||
MOV a5+40(FP), A4
|
||||
MOV a6+48(FP), A5
|
||||
MOV trap+0(FP), T0 // syscall entry
|
||||
ECALL
|
||||
BNE T0, ZERO, err
|
||||
MOV A0, r1+56(FP) // r1
|
||||
MOV A1, r2+64(FP) // r2
|
||||
MOV ZERO, err+72(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
err:
|
||||
MOV $-1, T0
|
||||
MOV T0, r1+56(FP) // r1
|
||||
MOV ZERO, r2+64(FP) // r2
|
||||
MOV A0, err+72(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV trap+0(FP), T0 // syscall entry
|
||||
ECALL
|
||||
BNE T0, ZERO, err
|
||||
MOV A0, r1+32(FP) // r1
|
||||
MOV A1, r2+40(FP) // r2
|
||||
MOV ZERO, err+48(FP) // errno
|
||||
RET
|
||||
err:
|
||||
MOV $-1, T0
|
||||
MOV T0, r1+32(FP) // r1
|
||||
MOV ZERO, r2+40(FP) // r2
|
||||
MOV A0, err+48(FP) // errno
|
||||
RET
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV a4+32(FP), A3
|
||||
MOV a5+40(FP), A4
|
||||
MOV a6+48(FP), A5
|
||||
MOV trap+0(FP), T0 // syscall entry
|
||||
ECALL
|
||||
BNE T0, ZERO, err
|
||||
MOV A0, r1+56(FP) // r1
|
||||
MOV A1, r2+64(FP) // r2
|
||||
MOV ZERO, err+72(FP) // errno
|
||||
RET
|
||||
err:
|
||||
MOV $-1, T0
|
||||
MOV T0, r1+56(FP) // r1
|
||||
MOV ZERO, r2+64(FP) // r2
|
||||
MOV A0, err+72(FP) // errno
|
||||
RET
|
||||
|
||||
// Actually Syscall7
|
||||
// func Syscall9(num uintptr, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV a4+32(FP), A3
|
||||
MOV a5+40(FP), A4
|
||||
MOV a6+48(FP), A5
|
||||
MOV a7+56(FP), A6
|
||||
MOV num+0(FP), T0 // syscall entry
|
||||
ECALL
|
||||
BNE T0, ZERO, err
|
||||
MOV A0, r1+80(FP) // r1
|
||||
MOV A1, r2+88(FP) // r2
|
||||
MOV ZERO, err+96(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
err:
|
||||
MOV $-1, T0
|
||||
MOV T0, r1+80(FP) // r1
|
||||
MOV ZERO, r2+88(FP) // r2
|
||||
MOV A0, err+96(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
123
src/syscall/asm_linux_386.s
Normal file
123
src/syscall/asm_linux_386.s
Normal file
@@ -0,0 +1,123 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System calls for 386, Linux
|
||||
//
|
||||
|
||||
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
||||
// instead of the glibc-specific "CALL 0x10(GS)".
|
||||
#define INVOKE_SYSCALL INT $0x80
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-24
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
POPL SI // preserve return address
|
||||
INVOKE_SYSCALL
|
||||
PUSHL SI
|
||||
CMPL AX, $0xfffff001
|
||||
JLS ok
|
||||
MOVL $-1, r1+16(FP)
|
||||
NEGL AX
|
||||
MOVL AX, err+20(FP)
|
||||
RET
|
||||
ok:
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL $0, err+20(FP)
|
||||
RET
|
||||
|
||||
// func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr);
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
MOVL a1+4(FP), BX
|
||||
MOVL a2+8(FP), CX
|
||||
MOVL a3+12(FP), DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL DX, r2+20(FP)
|
||||
RET
|
||||
|
||||
#define SYS_SOCKETCALL 102 /* from zsysnum_linux_386.go */
|
||||
|
||||
// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int)
|
||||
// Kernel interface gets call sub-number and pointer to a0.
|
||||
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL $SYS_SOCKETCALL, AX // syscall entry
|
||||
MOVL call+0(FP), BX // socket call number
|
||||
LEAL a0+4(FP), CX // pointer to call arguments
|
||||
MOVL $0, DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
CMPL AX, $0xfffff001
|
||||
JLS oksock
|
||||
MOVL $-1, n+28(FP)
|
||||
NEGL AX
|
||||
MOVL AX, err+32(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
oksock:
|
||||
MOVL AX, n+28(FP)
|
||||
MOVL $0, err+32(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int)
|
||||
// Kernel interface gets call sub-number and pointer to a0.
|
||||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
|
||||
MOVL $SYS_SOCKETCALL, AX // syscall entry
|
||||
MOVL call+0(FP), BX // socket call number
|
||||
LEAL a0+4(FP), CX // pointer to call arguments
|
||||
MOVL $0, DX
|
||||
MOVL $0, SI
|
||||
MOVL $0, DI
|
||||
INVOKE_SYSCALL
|
||||
CMPL AX, $0xfffff001
|
||||
JLS oksock1
|
||||
MOVL $-1, n+28(FP)
|
||||
NEGL AX
|
||||
MOVL AX, err+32(FP)
|
||||
RET
|
||||
oksock1:
|
||||
MOVL AX, n+28(FP)
|
||||
MOVL $0, err+32(FP)
|
||||
RET
|
||||
|
||||
#define SYS__LLSEEK 140 /* from zsysnum_linux_386.go */
|
||||
// func Seek(fd int, offset int64, whence int) (newoffset int64, err int)
|
||||
// Implemented in assembly to avoid allocation when
|
||||
// taking the address of the return value newoffset.
|
||||
// Underlying system call is
|
||||
// llseek(int fd, int offhi, int offlo, int64 *result, int whence)
|
||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL $SYS__LLSEEK, AX // syscall entry
|
||||
MOVL fd+0(FP), BX
|
||||
MOVL offset_hi+8(FP), CX
|
||||
MOVL offset_lo+4(FP), DX
|
||||
LEAL newoffset_lo+16(FP), SI // result pointer
|
||||
MOVL whence+12(FP), DI
|
||||
INVOKE_SYSCALL
|
||||
CMPL AX, $0xfffff001
|
||||
JLS okseek
|
||||
MOVL $-1, newoffset_lo+16(FP)
|
||||
MOVL $-1, newoffset_hi+20(FP)
|
||||
NEGL AX
|
||||
MOVL AX, err+24(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
okseek:
|
||||
// system call filled in newoffset already
|
||||
MOVL $0, err+24(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
68
src/syscall/asm_linux_amd64.s
Normal file
68
src/syscall/asm_linux_amd64.s
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System calls for AMD64, Linux
|
||||
//
|
||||
|
||||
#define SYS_gettimeofday 96
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-48
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ $0, R10
|
||||
MOVQ $0, R8
|
||||
MOVQ $0, R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
POPQ R12 // preserve return address
|
||||
SYSCALL
|
||||
PUSHQ R12
|
||||
CMPQ AX, $0xfffffffffffff001
|
||||
JLS ok2
|
||||
MOVQ $-1, r1+32(FP)
|
||||
NEGQ AX
|
||||
MOVQ AX, err+40(FP)
|
||||
RET
|
||||
ok2:
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ $0, err+40(FP)
|
||||
RET
|
||||
|
||||
// func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr)
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ DX, r2+40(FP)
|
||||
RET
|
||||
|
||||
// func gettimeofday(tv *Timeval) (err uintptr)
|
||||
TEXT ·gettimeofday(SB),NOSPLIT,$0-16
|
||||
MOVQ tv+0(FP), DI
|
||||
MOVQ $0, SI
|
||||
MOVQ runtime·vdsoGettimeofdaySym(SB), AX
|
||||
TESTQ AX, AX
|
||||
JZ fallback
|
||||
CALL AX
|
||||
ret:
|
||||
CMPQ AX, $0xfffffffffffff001
|
||||
JLS ok7
|
||||
NEGQ AX
|
||||
MOVQ AX, err+8(FP)
|
||||
RET
|
||||
fallback:
|
||||
MOVL $SYS_gettimeofday, AX
|
||||
SYSCALL
|
||||
JMP ret
|
||||
ok7:
|
||||
MOVQ $0, err+8(FP)
|
||||
RET
|
||||
75
src/syscall/asm_linux_arm.s
Normal file
75
src/syscall/asm_linux_arm.s
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System calls for arm, Linux
|
||||
//
|
||||
|
||||
#define SYS__LLSEEK 140 /* from zsysnum_linux_arm.go */
|
||||
// func seek(fd int, offset int64, whence int) (newoffset int64, errno int)
|
||||
// Implemented in assembly to avoid allocation when
|
||||
// taking the address of the return value newoffset.
|
||||
// Underlying system call is
|
||||
// llseek(int fd, int offhi, int offlo, int64 *result, int whence)
|
||||
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW $SYS__LLSEEK, R7 // syscall entry
|
||||
MOVW fd+0(FP), R0
|
||||
MOVW offset_hi+8(FP), R1
|
||||
MOVW offset_lo+4(FP), R2
|
||||
MOVW $newoffset_lo+16(FP), R3
|
||||
MOVW whence+12(FP), R4
|
||||
SWI $0
|
||||
MOVW $0xfffff001, R6
|
||||
CMP R6, R0
|
||||
BLS okseek
|
||||
MOVW $0, R1
|
||||
MOVW R1, newoffset_lo+16(FP)
|
||||
MOVW R1, newoffset_hi+20(FP)
|
||||
RSB $0, R0, R0
|
||||
MOVW R0, err+24(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
okseek:
|
||||
// system call filled in newoffset already
|
||||
MOVW $0, R0
|
||||
MOVW R0, err+24(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-24
|
||||
MOVW trap+0(FP), R7 // syscall entry
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
SWI $0
|
||||
MOVW $0xfffff001, R1
|
||||
CMP R1, R0
|
||||
BLS ok
|
||||
MOVW $-1, R1
|
||||
MOVW R1, r1+16(FP)
|
||||
RSB $0, R0, R0
|
||||
MOVW R0, err+20(FP)
|
||||
RET
|
||||
ok:
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW $0, R0
|
||||
MOVW R0, err+20(FP)
|
||||
RET
|
||||
|
||||
// func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr);
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-24
|
||||
MOVW trap+0(FP), R7 // syscall entry
|
||||
MOVW a1+4(FP), R0
|
||||
MOVW a2+8(FP), R1
|
||||
MOVW a3+12(FP), R2
|
||||
SWI $0
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW $0, R0
|
||||
MOVW R0, r2+20(FP)
|
||||
RET
|
||||
41
src/syscall/asm_linux_arm64.s
Normal file
41
src/syscall/asm_linux_arm64.s
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2015 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD $0, R3
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
SVC
|
||||
CMN $4095, R0
|
||||
BCC ok
|
||||
MOVD $-1, R4
|
||||
MOVD R4, r1+32(FP) // r1
|
||||
NEG R0, R0
|
||||
MOVD R0, err+40(FP) // errno
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD ZR, err+40(FP) // errno
|
||||
RET
|
||||
|
||||
// func rawSyscallNoError(trap uintptr, a1, a2, a3 uintptr) (r1, r2 uintptr);
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD $0, R3
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD trap+0(FP), R8 // syscall entry
|
||||
SVC
|
||||
MOVD R0, r1+32(FP)
|
||||
MOVD R1, r2+40(FP)
|
||||
RET
|
||||
44
src/syscall/asm_linux_loong64.s
Normal file
44
src/syscall/asm_linux_loong64.s
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for loong64, Linux
|
||||
//
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV $0, R7
|
||||
MOVV $0, R8
|
||||
MOVV $0, R9
|
||||
MOVV trap+0(FP), R11 // syscall entry
|
||||
SYSCALL
|
||||
MOVW $-4096, R12
|
||||
BGEU R12, R4, ok
|
||||
MOVV $-1, R12
|
||||
MOVV R12, r1+32(FP) // r1
|
||||
SUBVU R4, R0, R4
|
||||
MOVV R4, err+40(FP) // errno
|
||||
RET
|
||||
ok:
|
||||
MOVV R4, r1+32(FP) // r1
|
||||
MOVV R0, err+40(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R11 // syscall entry
|
||||
SYSCALL
|
||||
MOVV R4, r1+32(FP)
|
||||
MOVV R0, r2+40(FP) // r2 is not used. Always set to 0.
|
||||
RET
|
||||
45
src/syscall/asm_linux_mips64x.s
Normal file
45
src/syscall/asm_linux_mips64x.s
Normal file
@@ -0,0 +1,45 @@
|
||||
// Copyright 2014 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 && (mips64 || mips64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for mips64, Linux
|
||||
//
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
BEQ R7, ok
|
||||
MOVV $-1, R1
|
||||
MOVV R1, r1+32(FP) // r1
|
||||
MOVV R2, err+40(FP) // errno
|
||||
RET
|
||||
ok:
|
||||
MOVV R2, r1+32(FP) // r1
|
||||
MOVV R0, err+40(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
MOVV R0, R3 // reset R3 to zero as 1-ret SYSCALL keeps it
|
||||
SYSCALL
|
||||
MOVV R2, r1+32(FP) // r1
|
||||
MOVV R3, r2+40(FP) // r2
|
||||
RET
|
||||
74
src/syscall/asm_linux_mipsx.s
Normal file
74
src/syscall/asm_linux_mipsx.s
Normal file
@@ -0,0 +1,74 @@
|
||||
// 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.
|
||||
|
||||
//go:build linux && (mips || mipsle)
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System calls for mips, Linux
|
||||
//
|
||||
|
||||
// func Syscall9(trap trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr);
|
||||
// Actually Syscall8 but the rest of the code expects it to be named Syscall9.
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$28-52
|
||||
NO_LOCAL_POINTERS
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW a4+16(FP), R7
|
||||
MOVW a5+20(FP), R8
|
||||
MOVW a6+24(FP), R9
|
||||
MOVW a7+28(FP), R10
|
||||
MOVW a8+32(FP), R11
|
||||
MOVW R8, 16(R29)
|
||||
MOVW R9, 20(R29)
|
||||
MOVW R10, 24(R29)
|
||||
MOVW R11, 28(R29)
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
MOVW R0, R3 // reset R3 to zero as 1-ret SYSCALL keeps it
|
||||
SYSCALL
|
||||
BEQ R7, ok9
|
||||
MOVW $-1, R1
|
||||
MOVW R1, r1+40(FP) // r1
|
||||
MOVW R0, r2+44(FP) // r2
|
||||
MOVW R2, err+48(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok9:
|
||||
MOVW R2, r1+40(FP) // r1
|
||||
MOVW R3, r2+44(FP) // r2
|
||||
MOVW R0, err+48(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-24
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
BEQ R7, ok
|
||||
MOVW $-1, R1
|
||||
MOVW R1, r1+16(FP) // r1
|
||||
MOVW R2, err+20(FP) // errno
|
||||
RET
|
||||
ok:
|
||||
MOVW R2, r1+16(FP) // r1
|
||||
MOVW R0, err+20(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$20-24
|
||||
MOVW a1+4(FP), R4
|
||||
MOVW a2+8(FP), R5
|
||||
MOVW a3+12(FP), R6
|
||||
MOVW trap+0(FP), R2 // syscall entry
|
||||
MOVW R0, R3 // reset R3 to zero as 1-ret SYSCALL keeps it
|
||||
SYSCALL
|
||||
MOVW R2, r1+16(FP) // r1
|
||||
MOVW R3, r2+20(FP) // r2
|
||||
RET
|
||||
44
src/syscall/asm_linux_ppc64x.s
Normal file
44
src/syscall/asm_linux_ppc64x.s
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2014 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 && (ppc64 || ppc64le)
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for ppc64, Linux
|
||||
//
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-48
|
||||
MOVD a1+8(FP), R3
|
||||
MOVD a2+16(FP), R4
|
||||
MOVD a3+24(FP), R5
|
||||
MOVD R0, R6
|
||||
MOVD R0, R7
|
||||
MOVD R0, R8
|
||||
MOVD trap+0(FP), R9 // syscall entry
|
||||
SYSCALL R9
|
||||
BVC ok
|
||||
MOVD $-1, R4
|
||||
MOVD R4, r1+32(FP) // r1
|
||||
MOVD R3, err+40(FP) // errno
|
||||
RET
|
||||
ok:
|
||||
MOVD R3, r1+32(FP) // r1
|
||||
MOVD R0, err+40(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R3
|
||||
MOVD a2+16(FP), R4
|
||||
MOVD a3+24(FP), R5
|
||||
MOVD R0, R6
|
||||
MOVD R0, R7
|
||||
MOVD R0, R8
|
||||
MOVD trap+0(FP), R9 // syscall entry
|
||||
SYSCALL R9
|
||||
MOVD R3, r1+32(FP)
|
||||
MOVD R0, r2+40(FP)
|
||||
RET
|
||||
41
src/syscall/asm_linux_riscv64.s
Normal file
41
src/syscall/asm_linux_riscv64.s
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for riscv64, Linux
|
||||
//
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-48
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV ZERO, A3
|
||||
MOV ZERO, A4
|
||||
MOV ZERO, A5
|
||||
MOV trap+0(FP), A7 // syscall entry
|
||||
ECALL
|
||||
MOV $-4096, T0
|
||||
BLTU T0, A0, err
|
||||
MOV A0, r1+32(FP) // r1
|
||||
MOV ZERO, err+40(FP) // errno
|
||||
RET
|
||||
err:
|
||||
MOV $-1, T0
|
||||
MOV T0, r1+32(FP) // r1
|
||||
SUB A0, ZERO, A0
|
||||
MOV A0, err+40(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOV a1+8(FP), A0
|
||||
MOV a2+16(FP), A1
|
||||
MOV a3+24(FP), A2
|
||||
MOV trap+0(FP), A7 // syscall entry
|
||||
ECALL
|
||||
MOV A0, r1+32(FP)
|
||||
MOV A1, r2+40(FP)
|
||||
RET
|
||||
93
src/syscall/asm_linux_s390x.s
Normal file
93
src/syscall/asm_linux_s390x.s
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for s390x, Linux
|
||||
//
|
||||
|
||||
// func rawVforkSyscall(trap, a1, a2, a3 uintptr) (r1, err uintptr)
|
||||
TEXT ·rawVforkSyscall(SB),NOSPLIT|NOFRAME,$0-48
|
||||
MOVD a1+8(FP), R2
|
||||
MOVD a2+16(FP), R3
|
||||
MOVD a3+24(FP), R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
MOVD trap+0(FP), R1 // syscall entry
|
||||
SYSCALL
|
||||
MOVD $0xfffffffffffff001, R8
|
||||
CMPUBLT R2, R8, ok2
|
||||
MOVD $-1, r1+32(FP)
|
||||
NEG R2, R2
|
||||
MOVD R2, err+40(FP) // errno
|
||||
RET
|
||||
ok2:
|
||||
MOVD R2, r1+32(FP)
|
||||
MOVD $0, err+40(FP) // errno
|
||||
RET
|
||||
|
||||
// func rawSyscallNoError(trap, a1, a2, a3 uintptr) (r1, r2 uintptr)
|
||||
TEXT ·rawSyscallNoError(SB),NOSPLIT,$0-48
|
||||
MOVD a1+8(FP), R2
|
||||
MOVD a2+16(FP), R3
|
||||
MOVD a3+24(FP), R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
MOVD trap+0(FP), R1 // syscall entry
|
||||
SYSCALL
|
||||
MOVD R2, r1+32(FP)
|
||||
MOVD R3, r2+40(FP)
|
||||
RET
|
||||
|
||||
#define SYS_SOCKETCALL 102 /* from zsysnum_linux_s390x.go */
|
||||
|
||||
// func socketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int)
|
||||
// Kernel interface gets call sub-number and pointer to a0.
|
||||
TEXT ·socketcall(SB),NOSPLIT,$0-72
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVD $SYS_SOCKETCALL, R1 // syscall entry
|
||||
MOVD call+0(FP), R2 // socket call number
|
||||
MOVD $a0+8(FP), R3 // pointer to call arguments
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
SYSCALL
|
||||
MOVD $0xfffffffffffff001, R8
|
||||
CMPUBLT R2, R8, oksock
|
||||
MOVD $-1, n+56(FP)
|
||||
NEG R2, R2
|
||||
MOVD R2, err+64(FP)
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
oksock:
|
||||
MOVD R2, n+56(FP)
|
||||
MOVD $0, err+64(FP)
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func rawsocketcall(call int, a0, a1, a2, a3, a4, a5 uintptr) (n int, err int)
|
||||
// Kernel interface gets call sub-number and pointer to a0.
|
||||
TEXT ·rawsocketcall(SB),NOSPLIT,$0-72
|
||||
MOVD $SYS_SOCKETCALL, R1 // syscall entry
|
||||
MOVD call+0(FP), R2 // socket call number
|
||||
MOVD $a0+8(FP), R3 // pointer to call arguments
|
||||
MOVD $0, R4
|
||||
MOVD $0, R5
|
||||
MOVD $0, R6
|
||||
MOVD $0, R7
|
||||
SYSCALL
|
||||
MOVD $0xfffffffffffff001, R8
|
||||
CMPUBLT R2, R8, oksock1
|
||||
MOVD $-1, n+56(FP)
|
||||
NEG R2, R2
|
||||
MOVD R2, err+64(FP)
|
||||
RET
|
||||
oksock1:
|
||||
MOVD R2, n+56(FP)
|
||||
MOVD $0, err+64(FP)
|
||||
RET
|
||||
127
src/syscall/asm_netbsd_arm.s
Normal file
127
src/syscall/asm_netbsd_arm.s
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, NetBSD
|
||||
//
|
||||
|
||||
// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
|
||||
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
|
||||
// func Syscall9(trap int32, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int32)
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW trap+0(FP), R0 // sigcall num
|
||||
MOVW a1+4(FP), R1 // a1
|
||||
MOVW a2+8(FP), R2 // a2
|
||||
MOVW a3+12(FP), R3 // a3
|
||||
SWI $0 // syscall
|
||||
MOVW $0, R2
|
||||
BCS error
|
||||
MOVW R0, r1+16(FP) // r1
|
||||
MOVW R1, r2+20(FP) // r2
|
||||
MOVW R2, err+24(FP) // err
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
error:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+16(FP) // r1
|
||||
MOVW R2, r2+20(FP) // r2
|
||||
MOVW R0, err+24(FP) // err
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW trap+0(FP), R0 // sigcall num
|
||||
MOVW a1+4(FP), R1 // a1
|
||||
MOVW a2+8(FP), R2 // a2
|
||||
MOVW a3+12(FP), R3 // a3
|
||||
MOVW R13, R4
|
||||
MOVW $a4+16(FP), R13 // a4 to a6 are passed on stack
|
||||
SWI $0 // syscall
|
||||
MOVW R4, R13
|
||||
MOVW $0, R2
|
||||
BCS error6
|
||||
MOVW R0, r1+28(FP) // r1
|
||||
MOVW R1, r2+32(FP) // r2
|
||||
MOVW R2, err+36(FP) // err
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
error6:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+28(FP) // r1
|
||||
MOVW R2, r2+32(FP) // r2
|
||||
MOVW R0, err+36(FP) // err
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW num+0(FP), R0 // sigcall num
|
||||
MOVW a1+4(FP), R1 // a1
|
||||
MOVW a2+8(FP), R2 // a2
|
||||
MOVW a3+12(FP), R3 // a3
|
||||
MOVW R13, R4
|
||||
MOVW $a4+16(FP), R13 // a4 to a9 are passed on stack
|
||||
SWI $0 // syscall
|
||||
MOVW R4, R13
|
||||
MOVW $0, R2
|
||||
BCS error9
|
||||
MOVW R0, r1+40(FP) // r1
|
||||
MOVW R1, r2+44(FP) // r2
|
||||
MOVW R2, err+48(FP) // err
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
error9:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+40(FP) // r1
|
||||
MOVW R2, r2+44(FP) // r2
|
||||
MOVW R0, err+48(FP) // err
|
||||
BL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
MOVW trap+0(FP), R0 // sigcall num
|
||||
MOVW a1+4(FP), R1 // a1
|
||||
MOVW a2+8(FP), R2 // a2
|
||||
MOVW a3+12(FP), R3 // a3
|
||||
SWI $0 // syscall
|
||||
MOVW $0, R2
|
||||
BCS errorr
|
||||
MOVW R0, r1+16(FP) // r1
|
||||
MOVW R1, r2+20(FP) // r2
|
||||
MOVW R2, err+24(FP) // err
|
||||
RET
|
||||
errorr:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+16(FP) // r1
|
||||
MOVW R2, r2+20(FP) // r2
|
||||
MOVW R0, err+24(FP) // err
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
MOVW trap+0(FP), R0 // sigcall num
|
||||
MOVW a1+4(FP), R1 // a1
|
||||
MOVW a2+8(FP), R2 // a2
|
||||
MOVW a3+12(FP), R3 // a3
|
||||
MOVW R13, R4
|
||||
MOVW $a4+16(FP), R13 // a4 to a9 are passed on stack
|
||||
SWI $0 // syscall
|
||||
MOVW R4, R13
|
||||
MOVW $0, R2
|
||||
BCS errorr6
|
||||
MOVW R0, r1+28(FP) // r1
|
||||
MOVW R1, r2+32(FP) // r2
|
||||
MOVW R2, err+36(FP) // err
|
||||
RET
|
||||
errorr6:
|
||||
MOVW $-1, R3
|
||||
MOVW R3, r1+28(FP) // r1
|
||||
MOVW R2, r2+32(FP) // r2
|
||||
MOVW R0, err+36(FP) // err
|
||||
RET
|
||||
128
src/syscall/asm_netbsd_arm64.s
Normal file
128
src/syscall/asm_netbsd_arm64.s
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM64, NetBSD
|
||||
//
|
||||
|
||||
#define SYS_syscall 0
|
||||
|
||||
// func Syscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD trap+0(FP), R17
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+32(FP) // r1
|
||||
MOVD ZR, r2+40(FP) // r2
|
||||
MOVD R0, err+48(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD R1, r2+40(FP) // r2
|
||||
MOVD ZR, err+48(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall(trap uintptr, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVD trap+0(FP), R17 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+32(FP) // r1
|
||||
MOVD ZR, r2+40(FP) // r2
|
||||
MOVD R0, err+48(FP) // err
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+32(FP) // r1
|
||||
MOVD R1, r2+40(FP) // r2
|
||||
MOVD ZR, err+48(FP) // err
|
||||
RET
|
||||
|
||||
// func Syscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD trap+0(FP), R17 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+56(FP) // r1
|
||||
MOVD ZR, r2+64(FP) // r2
|
||||
MOVD R0, err+72(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+56(FP) // r1
|
||||
MOVD R1, r2+64(FP) // r2
|
||||
MOVD ZR, err+72(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
// func RawSyscall6(trap uintptr, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVD trap+0(FP), R17 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+56(FP) // r1
|
||||
MOVD ZR, r2+64(FP) // r2
|
||||
MOVD R0, err+72(FP) // err
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+56(FP) // r1
|
||||
MOVD R1, r2+64(FP) // r2
|
||||
MOVD ZR, R0
|
||||
MOVD R0, err+72(FP) // err
|
||||
RET
|
||||
|
||||
// Actually Syscall7
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
BL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVD num+0(FP), R17 // syscall entry
|
||||
MOVD a1+8(FP), R0
|
||||
MOVD a2+16(FP), R1
|
||||
MOVD a3+24(FP), R2
|
||||
MOVD a4+32(FP), R3
|
||||
MOVD a5+40(FP), R4
|
||||
MOVD a6+48(FP), R5
|
||||
MOVD a7+56(FP), R6
|
||||
//MOVD a8+64(FP), R7
|
||||
//MOVD a9+72(FP), R8
|
||||
SVC $SYS_syscall
|
||||
BCC ok
|
||||
MOVD $-1, R1
|
||||
MOVD R1, r1+80(FP) // r1
|
||||
MOVD ZR, r2+88(FP) // r2
|
||||
MOVD R0, err+96(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVD R0, r1+80(FP) // r1
|
||||
MOVD R1, r2+88(FP) // r2
|
||||
MOVD ZR, err+96(FP) // err
|
||||
BL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
32
src/syscall/asm_openbsd_386.s
Normal file
32
src/syscall/asm_openbsd_386.s
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2021 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for 386, OpenBSD
|
||||
//
|
||||
|
||||
// Provide these function names via assembly so they are provided as ABI0,
|
||||
// rather than ABIInternal.
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP ·syscallInternal(SB)
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP ·syscall6Internal(SB)
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP ·rawSyscallInternal(SB)
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP ·rawSyscall6Internal(SB)
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP ·syscall9Internal(SB)
|
||||
32
src/syscall/asm_openbsd_amd64.s
Normal file
32
src/syscall/asm_openbsd_amd64.s
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64, OpenBSD
|
||||
//
|
||||
|
||||
// Provide these function names via assembly so they are provided as ABI0,
|
||||
// rather than ABIInternal.
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP ·syscallInternal(SB)
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·syscall6Internal(SB)
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP ·rawSyscallInternal(SB)
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·rawSyscall6Internal(SB)
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP ·syscall9Internal(SB)
|
||||
32
src/syscall/asm_openbsd_arm.s
Normal file
32
src/syscall/asm_openbsd_arm.s
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2013 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM, OpenBSD
|
||||
//
|
||||
|
||||
// Provide these function names via assembly so they are provided as ABI0,
|
||||
// rather than ABIInternal.
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
JMP ·syscallInternal(SB)
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
JMP ·syscall6Internal(SB)
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
JMP ·rawSyscallInternal(SB)
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
JMP ·rawSyscall6Internal(SB)
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
JMP ·syscall9Internal(SB)
|
||||
32
src/syscall/asm_openbsd_arm64.s
Normal file
32
src/syscall/asm_openbsd_arm64.s
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for ARM64, OpenBSD
|
||||
//
|
||||
|
||||
// Provide these function names via assembly so they are provided as ABI0,
|
||||
// rather than ABIInternal.
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP ·syscallInternal(SB)
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·syscall6Internal(SB)
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP ·rawSyscallInternal(SB)
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·rawSyscall6Internal(SB)
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP ·syscall9Internal(SB)
|
||||
129
src/syscall/asm_openbsd_mips64.s
Normal file
129
src/syscall/asm_openbsd_mips64.s
Normal file
@@ -0,0 +1,129 @@
|
||||
// Copyright 2020 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64);
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
BEQ R7, ok
|
||||
MOVV $-1, R1
|
||||
MOVV R1, r1+32(FP) // r1
|
||||
MOVV R0, r2+40(FP) // r2
|
||||
MOVV R2, err+48(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVV R2, r1+32(FP) // r1
|
||||
MOVV R3, r2+40(FP) // r2
|
||||
MOVV R0, err+48(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV a4+32(FP), R7
|
||||
MOVV a5+40(FP), R8
|
||||
MOVV a6+48(FP), R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
BEQ R7, ok6
|
||||
MOVV $-1, R1
|
||||
MOVV R1, r1+56(FP) // r1
|
||||
MOVV R0, r2+64(FP) // r2
|
||||
MOVV R2, err+72(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok6:
|
||||
MOVV R2, r1+56(FP) // r1
|
||||
MOVV R3, r2+64(FP) // r2
|
||||
MOVV R0, err+72(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
// func Syscall9(trap int64, a1, a2, a3, a4, a5, a6, a7, a8, a9 int64) (r1, r2, err int64);
|
||||
// The openbsd/mips64 kernel only accepts eight syscall arguments, except
|
||||
// for SYS_syscall, where an additional argument can be passed on the stack.
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JAL runtime·entersyscall(SB)
|
||||
MOVV num+0(FP), R2 // syscall entry
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV a4+32(FP), R7
|
||||
MOVV a5+40(FP), R8
|
||||
MOVV a6+48(FP), R9
|
||||
MOVV a7+56(FP), R10
|
||||
MOVV a8+64(FP), R11
|
||||
MOVV a9+72(FP), R12
|
||||
SUBVU $16, R29
|
||||
MOVV R12, 0(R29) // arg 9 - only used for SYS_syscall.
|
||||
SYSCALL
|
||||
ADDV $16, R29
|
||||
BEQ R7, ok9
|
||||
MOVV $-1, R1
|
||||
MOVV R1, r1+80(FP) // r1
|
||||
MOVV R0, r2+88(FP) // r2
|
||||
MOVV R2, err+96(FP) // errno
|
||||
JAL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok9:
|
||||
MOVV R2, r1+80(FP) // r1
|
||||
MOVV R3, r2+88(FP) // r2
|
||||
MOVV R0, err+96(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV R0, R7
|
||||
MOVV R0, R8
|
||||
MOVV R0, R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
BEQ R7, ok1
|
||||
MOVV $-1, R1
|
||||
MOVV R1, r1+32(FP) // r1
|
||||
MOVV R0, r2+40(FP) // r2
|
||||
MOVV R2, err+48(FP) // errno
|
||||
RET
|
||||
ok1:
|
||||
MOVV R2, r1+32(FP) // r1
|
||||
MOVV R3, r2+40(FP) // r2
|
||||
MOVV R0, err+48(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVV a1+8(FP), R4
|
||||
MOVV a2+16(FP), R5
|
||||
MOVV a3+24(FP), R6
|
||||
MOVV a4+32(FP), R7
|
||||
MOVV a5+40(FP), R8
|
||||
MOVV a6+48(FP), R9
|
||||
MOVV trap+0(FP), R2 // syscall entry
|
||||
SYSCALL
|
||||
BEQ R7, ok2
|
||||
MOVV $-1, R1
|
||||
MOVV R1, r1+56(FP) // r1
|
||||
MOVV R0, r2+64(FP) // r2
|
||||
MOVV R2, err+72(FP) // errno
|
||||
RET
|
||||
ok2:
|
||||
MOVV R2, r1+56(FP) // r1
|
||||
MOVV R3, r2+64(FP) // r2
|
||||
MOVV R0, err+72(FP) // errno
|
||||
RET
|
||||
32
src/syscall/asm_openbsd_ppc64.s
Normal file
32
src/syscall/asm_openbsd_ppc64.s
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for PPC64, OpenBSD
|
||||
//
|
||||
|
||||
// Provide these function names via assembly so they are provided as ABI0,
|
||||
// rather than ABIInternal.
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP ·syscallInternal(SB)
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·syscall6Internal(SB)
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP ·rawSyscallInternal(SB)
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·rawSyscall6Internal(SB)
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP ·syscall9Internal(SB)
|
||||
32
src/syscall/asm_openbsd_riscv64.s
Normal file
32
src/syscall/asm_openbsd_riscv64.s
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright 2023 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System call support for RISCV64, OpenBSD
|
||||
//
|
||||
|
||||
// Provide these function names via assembly so they are provided as ABI0,
|
||||
// rather than ABIInternal.
|
||||
|
||||
// func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
JMP ·syscallInternal(SB)
|
||||
|
||||
// func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·syscall6Internal(SB)
|
||||
|
||||
// func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
JMP ·rawSyscallInternal(SB)
|
||||
|
||||
// func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
JMP ·rawSyscall6Internal(SB)
|
||||
|
||||
// func Syscall9(trap, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||
JMP ·syscall9Internal(SB)
|
||||
178
src/syscall/asm_plan9_386.s
Normal file
178
src/syscall/asm_plan9_386.s
Normal file
@@ -0,0 +1,178 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for 386, Plan 9
|
||||
//
|
||||
|
||||
//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
|
||||
//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
|
||||
//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
|
||||
#define SYS_ERRSTR 41 /* from zsysnum_plan9.go */
|
||||
|
||||
// Trap # in AX, args on stack above caller pc.
|
||||
TEXT ·Syscall(SB),NOSPLIT,$148-32
|
||||
NO_LOCAL_POINTERS
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// copy args down
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL sysargs-144(SP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $64
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL $0, r2+20(FP)
|
||||
CMPL AX, $-1
|
||||
JNE ok3
|
||||
|
||||
LEAL errbuf-128(SP), AX
|
||||
MOVL AX, sysargs-144(SP)
|
||||
MOVL $128, sysargs1-140(SP)
|
||||
MOVL $SYS_ERRSTR, AX
|
||||
INT $64
|
||||
CALL runtime·exitsyscall(SB)
|
||||
MOVL sysargs-144(SP), AX
|
||||
MOVL AX, errbuf-148(SP)
|
||||
CALL runtime·gostring(SB)
|
||||
LEAL str-144(SP), SI
|
||||
JMP copyresult3
|
||||
|
||||
ok3:
|
||||
CALL runtime·exitsyscall(SB)
|
||||
LEAL ·emptystring(SB), SI
|
||||
|
||||
copyresult3:
|
||||
LEAL err+24(FP), DI
|
||||
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$148-44
|
||||
NO_LOCAL_POINTERS
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// copy args down
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL sysargs-144(SP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $64
|
||||
MOVL AX, r1+28(FP)
|
||||
MOVL $0, r2+32(FP)
|
||||
CMPL AX, $-1
|
||||
JNE ok4
|
||||
|
||||
LEAL errbuf-128(SP), AX
|
||||
MOVL AX, sysargs-144(SP)
|
||||
MOVL $128, sysargs1-140(SP)
|
||||
MOVL $SYS_ERRSTR, AX
|
||||
INT $64
|
||||
CALL runtime·exitsyscall(SB)
|
||||
MOVL sysargs-144(SP), AX
|
||||
MOVL AX, errbuf-148(SP)
|
||||
CALL runtime·gostring(SB)
|
||||
LEAL str-144(SP), SI
|
||||
JMP copyresult4
|
||||
|
||||
ok4:
|
||||
CALL runtime·exitsyscall(SB)
|
||||
LEAL ·emptystring(SB), SI
|
||||
|
||||
copyresult4:
|
||||
LEAL err+36(FP), DI
|
||||
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL trap+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $64
|
||||
MOVL AX, r1+16(FP)
|
||||
MOVL AX, r2+20(FP)
|
||||
MOVL AX, err+24(FP)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL trap+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $64
|
||||
MOVL AX, r1+28(FP)
|
||||
MOVL AX, r2+32(FP)
|
||||
MOVL AX, err+36(FP)
|
||||
RET
|
||||
|
||||
#define SYS_SEEK 39 /* from zsysnum_plan9.go */
|
||||
|
||||
//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
|
||||
TEXT ·seek(SB),NOSPLIT,$24-36
|
||||
NO_LOCAL_POINTERS
|
||||
LEAL newoffset+20(FP), AX
|
||||
MOVL AX, placeholder+0(FP)
|
||||
|
||||
// copy args down
|
||||
LEAL placeholder+0(FP), SI
|
||||
LEAL sysargs-20(SP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVL $SYS_SEEK, AX // syscall entry
|
||||
INT $64
|
||||
|
||||
CMPL AX, $-1
|
||||
JNE ok6
|
||||
MOVL AX, newoffset_lo+20(FP)
|
||||
MOVL AX, newoffset_hi+24(FP)
|
||||
|
||||
CALL syscall·errstr(SB)
|
||||
MOVL SP, SI
|
||||
JMP copyresult6
|
||||
|
||||
ok6:
|
||||
LEAL ·emptystring(SB), SI
|
||||
|
||||
copyresult6:
|
||||
LEAL err+28(FP), DI
|
||||
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
RET
|
||||
176
src/syscall/asm_plan9_amd64.s
Normal file
176
src/syscall/asm_plan9_amd64.s
Normal file
@@ -0,0 +1,176 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for Plan 9
|
||||
//
|
||||
|
||||
//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
|
||||
//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
|
||||
//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
|
||||
#define SYS_ERRSTR 41 /* from zsysnum_plan9.go */
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$168-64
|
||||
NO_LOCAL_POINTERS
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ trap+0(FP), BP // syscall entry
|
||||
// copy args down
|
||||
LEAQ a1+8(FP), SI
|
||||
LEAQ sysargs-160(SP), DI
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ $0, r2+40(FP)
|
||||
CMPL AX, $-1
|
||||
JNE ok3
|
||||
|
||||
LEAQ errbuf-128(SP), AX
|
||||
MOVQ AX, sysargs-160(SP)
|
||||
MOVQ $128, sysargs1-152(SP)
|
||||
MOVQ $SYS_ERRSTR, BP
|
||||
SYSCALL
|
||||
CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set
|
||||
MOVQ sysargs-160(SP), AX
|
||||
MOVQ AX, errbuf-168(SP)
|
||||
CALL runtime·gostring(SB)
|
||||
LEAQ str-160(SP), SI
|
||||
JMP copyresult3
|
||||
|
||||
ok3:
|
||||
CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set
|
||||
LEAQ ·emptystring(SB), SI
|
||||
|
||||
copyresult3:
|
||||
LEAQ err+48(FP), DI
|
||||
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$168-88
|
||||
NO_LOCAL_POINTERS
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ trap+0(FP), BP // syscall entry
|
||||
// copy args down
|
||||
LEAQ a1+8(FP), SI
|
||||
LEAQ sysargs-160(SP), DI
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
SYSCALL
|
||||
MOVQ AX, r1+56(FP)
|
||||
MOVQ $0, r2+64(FP)
|
||||
CMPL AX, $-1
|
||||
JNE ok4
|
||||
|
||||
LEAQ errbuf-128(SP), AX
|
||||
MOVQ AX, sysargs-160(SP)
|
||||
MOVQ $128, sysargs1-152(SP)
|
||||
MOVQ $SYS_ERRSTR, BP
|
||||
SYSCALL
|
||||
CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set
|
||||
MOVQ sysargs-160(SP), AX
|
||||
MOVQ AX, errbuf-168(SP)
|
||||
CALL runtime·gostring(SB)
|
||||
LEAQ str-160(SP), SI
|
||||
JMP copyresult4
|
||||
|
||||
ok4:
|
||||
CALL runtime·exitsyscall(SB) // call via ABI wrapper, ensuring ABIInternal fixed registers are set
|
||||
LEAQ ·emptystring(SB), SI
|
||||
|
||||
copyresult4:
|
||||
LEAQ err+72(FP), DI
|
||||
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVQ trap+0(FP), BP // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAQ a1+8(FP), SI
|
||||
LEAQ trap+0(FP), DI
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
SYSCALL
|
||||
MOVQ AX, r1+32(FP)
|
||||
MOVQ AX, r2+40(FP)
|
||||
MOVQ AX, err+48(FP)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVQ trap+0(FP), BP // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAQ a1+8(FP), SI
|
||||
LEAQ trap+0(FP), DI
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
SYSCALL
|
||||
MOVQ AX, r1+56(FP)
|
||||
MOVQ AX, r2+64(FP)
|
||||
MOVQ AX, err+72(FP)
|
||||
RET
|
||||
|
||||
#define SYS_SEEK 39 /* from zsysnum_plan9.go */
|
||||
|
||||
//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
|
||||
TEXT ·seek(SB),NOSPLIT,$48-56
|
||||
NO_LOCAL_POINTERS
|
||||
LEAQ newoffset+32(FP), AX
|
||||
MOVQ AX, placeholder+0(FP)
|
||||
|
||||
// copy args down
|
||||
LEAQ placeholder+0(FP), SI
|
||||
LEAQ sysargs-40(SP), DI
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
MOVQ $SYS_SEEK, BP // syscall entry
|
||||
SYSCALL
|
||||
|
||||
CMPL AX, $-1
|
||||
JNE ok6
|
||||
MOVQ AX, newoffset+32(FP)
|
||||
|
||||
CALL syscall·errstr(SB)
|
||||
MOVQ SP, SI
|
||||
JMP copyresult6
|
||||
|
||||
ok6:
|
||||
LEAQ ·emptystring(SB), SI
|
||||
|
||||
copyresult6:
|
||||
LEAQ err+40(FP), DI
|
||||
|
||||
CLD
|
||||
MOVSQ
|
||||
MOVSQ
|
||||
RET
|
||||
132
src/syscall/asm_plan9_arm.s
Normal file
132
src/syscall/asm_plan9_arm.s
Normal file
@@ -0,0 +1,132 @@
|
||||
// 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.
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
#define SYS_ERRSTR 41 /* from zsysnum_plan9.go */
|
||||
#define SYS_SEEK 39 /* from zsysnum_plan9.go */
|
||||
|
||||
// System call support for plan9 on arm
|
||||
|
||||
//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err ErrorString)
|
||||
TEXT ·Syscall(SB),NOSPLIT,$144-32
|
||||
NO_LOCAL_POINTERS
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW $a1+4(FP), R0 // move syscall args
|
||||
MOVW $sysargs-144(SP), R1
|
||||
MOVM.IA (R0), [R2-R4]
|
||||
MOVM.IA [R2-R4], (R1)
|
||||
MOVW trap+0(FP), R0 // syscall num
|
||||
SWI $0
|
||||
MOVW $0, R2
|
||||
MOVW $r1+16(FP), R3
|
||||
MOVM.IA [R0,R2], (R3)
|
||||
CMP $-1, R0
|
||||
B.EQ syscallerr
|
||||
BL runtime·exitsyscall(SB)
|
||||
MOVW $·emptystring+0(SB), R2
|
||||
B syscallok
|
||||
syscallerr:
|
||||
MOVW $errbuf-128(SP), R2
|
||||
MOVW $128, R3
|
||||
MOVM.IA [R2,R3], (R1)
|
||||
MOVW $SYS_ERRSTR, R0
|
||||
SWI $0
|
||||
BL runtime·exitsyscall(SB)
|
||||
BL runtime·gostring(SB)
|
||||
MOVW $str-140(SP), R2
|
||||
syscallok:
|
||||
MOVW $err+24(FP), R1
|
||||
MOVM.IA (R2), [R3-R4]
|
||||
MOVM.IA [R3-R4], (R1)
|
||||
RET
|
||||
|
||||
|
||||
//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err ErrorString)
|
||||
// Actually Syscall5 but the rest of the code expects it to be named Syscall6.
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$144-44
|
||||
NO_LOCAL_POINTERS
|
||||
BL runtime·entersyscall(SB)
|
||||
MOVW $a1+4(FP), R0 // move syscall args
|
||||
MOVW $sysargs-144(SP), R1
|
||||
MOVM.IA (R0), [R2-R6]
|
||||
MOVM.IA [R2-R6], (R1)
|
||||
MOVW trap+0(FP), R0 // syscall num
|
||||
SWI $0
|
||||
MOVW $0, R2
|
||||
MOVW $r1+28(FP), R3
|
||||
MOVM.IA.W [R0,R2], (R3)
|
||||
CMP $-1, R0
|
||||
B.EQ syscall6err
|
||||
BL runtime·exitsyscall(SB)
|
||||
MOVW $·emptystring+0(SB), R2
|
||||
B syscall6ok
|
||||
syscall6err:
|
||||
MOVW $errbuf-128(SP), R2
|
||||
MOVW $128, R3
|
||||
MOVM.IA [R2,R3], (R1)
|
||||
MOVW $SYS_ERRSTR, R0
|
||||
SWI $0
|
||||
BL runtime·exitsyscall(SB)
|
||||
BL runtime·gostring(SB)
|
||||
MOVW $str-140(SP), R2
|
||||
syscall6ok:
|
||||
MOVW $err+36(FP), R1
|
||||
MOVM.IA (R2), [R3-R4]
|
||||
MOVM.IA [R3-R4], (R1)
|
||||
RET
|
||||
|
||||
//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$12-28
|
||||
MOVW $a1+4(FP), R0 // move syscall args
|
||||
MOVW $sysargs-12(SP), R1
|
||||
MOVM.IA (R0), [R2-R4]
|
||||
MOVM.IA [R2-R4], (R1)
|
||||
MOVW trap+0(FP), R0 // syscall num
|
||||
SWI $0
|
||||
MOVW R0, r1+16(FP)
|
||||
MOVW R0, r2+20(FP)
|
||||
MOVW R0, err+24(FP)
|
||||
RET
|
||||
|
||||
//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||
// Actually RawSyscall5 but the rest of the code expects it to be named RawSyscall6.
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$20-40
|
||||
MOVW $a1+4(FP), R0 // move syscall args
|
||||
MOVW $sysargs-20(SP), R1
|
||||
MOVM.IA (R0), [R2-R6]
|
||||
MOVM.IA [R2-R6], (R1)
|
||||
MOVW trap+0(FP), R0 // syscall num
|
||||
SWI $0
|
||||
MOVW R0, r1+28(FP)
|
||||
MOVW R0, r2+32(FP)
|
||||
MOVW R0, err+36(FP)
|
||||
RET
|
||||
|
||||
//func seek(placeholder uintptr, fd int, offset int64, whence int) (newoffset int64, err string)
|
||||
TEXT ·seek(SB),NOSPLIT,$20-36
|
||||
NO_LOCAL_POINTERS
|
||||
MOVW $newoffset_lo+20(FP), R6
|
||||
MOVW R6, sysargs-20(SP) // dest for return value
|
||||
MOVW $fd+4(FP), R0 // move syscall args
|
||||
MOVW $sysarg1-16(SP), R1
|
||||
MOVM.IA (R0), [R2-R5]
|
||||
MOVM.IA [R2-R5], (R1)
|
||||
MOVW $SYS_SEEK, R0 // syscall num
|
||||
SWI $0
|
||||
CMP $-1, R0
|
||||
B.EQ seekerr
|
||||
MOVW $·emptystring+0(SB), R2
|
||||
B seekok
|
||||
seekerr:
|
||||
MOVW R0, 0(R6)
|
||||
MOVW R0, 4(R6)
|
||||
BL ·errstr(SB)
|
||||
MOVW $ret-20(SP), R2
|
||||
seekok:
|
||||
MOVW $err+28(FP), R1
|
||||
MOVM.IA (R2), [R3-R4]
|
||||
MOVM.IA [R3-R4], (R1)
|
||||
RET
|
||||
82
src/syscall/asm_solaris_amd64.s
Normal file
82
src/syscall/asm_solaris_amd64.s
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
//
|
||||
// System calls for solaris/amd64 are implemented in ../runtime/syscall_solaris.go
|
||||
//
|
||||
|
||||
TEXT ·sysvicall6(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_sysvicall6(SB)
|
||||
|
||||
TEXT ·rawSysvicall6(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_rawsysvicall6(SB)
|
||||
|
||||
TEXT ·chdir(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_chdir(SB)
|
||||
|
||||
TEXT ·chroot1(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_chroot(SB)
|
||||
|
||||
TEXT ·closeFD(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_close(SB)
|
||||
|
||||
TEXT ·dup2child(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_dup2(SB)
|
||||
RET
|
||||
|
||||
TEXT ·execve(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_execve(SB)
|
||||
|
||||
TEXT ·exit(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_exit(SB)
|
||||
|
||||
TEXT ·fcntl1(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_fcntl(SB)
|
||||
|
||||
TEXT ·forkx(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_forkx(SB)
|
||||
|
||||
TEXT ·gethostname(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_gethostname(SB)
|
||||
|
||||
TEXT ·getpid(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_getpid(SB)
|
||||
|
||||
TEXT ·ioctl(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_ioctl(SB)
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_rawsyscall(SB)
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_rawsyscall6(SB)
|
||||
|
||||
TEXT ·setgid(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_setgid(SB)
|
||||
|
||||
TEXT ·setgroups1(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_setgroups(SB)
|
||||
|
||||
TEXT ·setrlimit1(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_setrlimit(SB)
|
||||
|
||||
TEXT ·setsid(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_setsid(SB)
|
||||
|
||||
TEXT ·setuid(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_setuid(SB)
|
||||
|
||||
TEXT ·setpgid(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_setpgid(SB)
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_syscall(SB)
|
||||
|
||||
TEXT ·wait4(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_wait4(SB)
|
||||
|
||||
TEXT ·write1(SB),NOSPLIT,$0
|
||||
JMP runtime·syscall_write(SB)
|
||||
142
src/syscall/asm_unix_386.s
Normal file
142
src/syscall/asm_unix_386.s
Normal file
@@ -0,0 +1,142 @@
|
||||
// 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 freebsd || netbsd
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for some 386 unixes
|
||||
//
|
||||
|
||||
// func Syscall(trap int32, a1, a2, a3 int32) (r1, r2, err int32);
|
||||
// func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);
|
||||
// Trap # in AX, args on stack above caller pc.
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL trap+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $0x80
|
||||
JAE ok
|
||||
MOVL $-1, r1+16(FP) // r1
|
||||
MOVL $-1, r2+20(FP) // r2
|
||||
MOVL AX, err+24(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVL AX, r1+16(FP) // r1
|
||||
MOVL DX, r2+20(FP) // r2
|
||||
MOVL $0, err+24(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL trap+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $0x80
|
||||
JAE ok6
|
||||
MOVL $-1, r1+28(FP) // r1
|
||||
MOVL $-1, r2+32(FP) // r2
|
||||
MOVL AX, err+36(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok6:
|
||||
MOVL AX, r1+28(FP) // r1
|
||||
MOVL DX, r2+32(FP) // r2
|
||||
MOVL $0, err+36(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||
CALL runtime·entersyscall(SB)
|
||||
MOVL num+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL num+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $0x80
|
||||
JAE ok9
|
||||
MOVL $-1, r1+40(FP) // r1
|
||||
MOVL $-1, r2+44(FP) // r2
|
||||
MOVL AX, err+48(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
ok9:
|
||||
MOVL AX, r1+40(FP) // r1
|
||||
MOVL DX, r2+44(FP) // r2
|
||||
MOVL $0, err+48(FP) // errno
|
||||
CALL runtime·exitsyscall(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL trap+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $0x80
|
||||
JAE ok1
|
||||
MOVL $-1, r1+16(FP) // r1
|
||||
MOVL $-1, r2+20(FP) // r2
|
||||
MOVL AX, err+24(FP) // errno
|
||||
RET
|
||||
ok1:
|
||||
MOVL AX, r1+16(FP) // r1
|
||||
MOVL DX, r2+20(FP) // r2
|
||||
MOVL $0, err+24(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||
MOVL trap+0(FP), AX // syscall entry
|
||||
// slide args down on top of system call number
|
||||
LEAL a1+4(FP), SI
|
||||
LEAL trap+0(FP), DI
|
||||
CLD
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
MOVSL
|
||||
INT $0x80
|
||||
JAE ok2
|
||||
MOVL $-1, r1+28(FP) // r1
|
||||
MOVL $-1, r2+32(FP) // r2
|
||||
MOVL AX, err+36(FP) // errno
|
||||
RET
|
||||
ok2:
|
||||
MOVL AX, r1+28(FP) // r1
|
||||
MOVL DX, r2+32(FP) // r2
|
||||
MOVL $0, err+36(FP) // errno
|
||||
RET
|
||||
96
src/syscall/asm_unix_amd64.s
Normal file
96
src/syscall/asm_unix_amd64.s
Normal file
@@ -0,0 +1,96 @@
|
||||
// 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 dragonfly || freebsd || netbsd
|
||||
|
||||
#include "textflag.h"
|
||||
#include "funcdata.h"
|
||||
|
||||
//
|
||||
// System call support for AMD64 unixes
|
||||
//
|
||||
|
||||
// func Syscall(trap int64, a1, a2, a3 int64) (r1, r2, err int64)
|
||||
// func Syscall6(trap int64, a1, a2, a3, a4, a5, a6 int64) (r1, r2, err int64)
|
||||
// Trap # in AX, args in DI SI DX, return in AX DX
|
||||
|
||||
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
SYSCALL
|
||||
JCC ok
|
||||
MOVQ $-1, r1+32(FP) // r1
|
||||
MOVQ $0, r2+40(FP) // r2
|
||||
MOVQ AX, err+48(FP) // errno
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok:
|
||||
MOVQ AX, r1+32(FP) // r1
|
||||
MOVQ DX, r2+40(FP) // r2
|
||||
MOVQ $0, err+48(FP) // errno
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||
CALL runtime·entersyscall<ABIInternal>(SB)
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), R10
|
||||
MOVQ a5+40(FP), R8
|
||||
MOVQ a6+48(FP), R9
|
||||
SYSCALL
|
||||
JCC ok6
|
||||
MOVQ $-1, r1+56(FP) // r1
|
||||
MOVQ $0, r2+64(FP) // r2
|
||||
MOVQ AX, err+72(FP) // errno
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
ok6:
|
||||
MOVQ AX, r1+56(FP) // r1
|
||||
MOVQ DX, r2+64(FP) // r2
|
||||
MOVQ $0, err+72(FP) // errno
|
||||
CALL runtime·exitsyscall<ABIInternal>(SB)
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
JCC ok1
|
||||
MOVQ $-1, r1+32(FP) // r1
|
||||
MOVQ $0, r2+40(FP) // r2
|
||||
MOVQ AX, err+48(FP) // errno
|
||||
RET
|
||||
ok1:
|
||||
MOVQ AX, r1+32(FP) // r1
|
||||
MOVQ DX, r2+40(FP) // r2
|
||||
MOVQ $0, err+48(FP) // errno
|
||||
RET
|
||||
|
||||
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||
MOVQ a1+8(FP), DI
|
||||
MOVQ a2+16(FP), SI
|
||||
MOVQ a3+24(FP), DX
|
||||
MOVQ a4+32(FP), R10
|
||||
MOVQ a5+40(FP), R8
|
||||
MOVQ a6+48(FP), R9
|
||||
MOVQ trap+0(FP), AX // syscall entry
|
||||
SYSCALL
|
||||
JCC ok2
|
||||
MOVQ $-1, r1+56(FP) // r1
|
||||
MOVQ $0, r2+64(FP) // r2
|
||||
MOVQ AX, err+72(FP) // errno
|
||||
RET
|
||||
ok2:
|
||||
MOVQ AX, r1+56(FP) // r1
|
||||
MOVQ DX, r2+64(FP) // r2
|
||||
MOVQ $0, err+72(FP) // errno
|
||||
RET
|
||||
22
src/syscall/badlinkname_unix.go
Normal file
22
src/syscall/badlinkname_unix.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris
|
||||
|
||||
package syscall
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// As of Go 1.22, the symbols below are found to be pulled via
|
||||
// linkname in the wild. We provide a push linkname here, to
|
||||
// keep them accessible with pull linknames.
|
||||
// This may change in the future. Please do not depend on them
|
||||
// in new code.
|
||||
|
||||
// golang.org/x/sys linknames getsockopt.
|
||||
// Do not remove or change the type signature.
|
||||
//
|
||||
//go:linkname getsockopt
|
||||
|
||||
//go:linkname setsockopt
|
||||
187
src/syscall/bpf_bsd.go
Normal file
187
src/syscall/bpf_bsd.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2018 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
|
||||
|
||||
// Berkeley packet filter for BSD variants
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfStmt(code, k int) *BpfInsn {
|
||||
return &BpfInsn{Code: uint16(code), K: uint32(k)}
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfJump(code, k, jt, jf int) *BpfInsn {
|
||||
return &BpfInsn{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)}
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfBuflen(fd int) (int, error) {
|
||||
var l int
|
||||
err := ioctlPtr(fd, BIOCGBLEN, unsafe.Pointer(&l))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfBuflen(fd, l int) (int, error) {
|
||||
err := ioctlPtr(fd, BIOCSBLEN, unsafe.Pointer(&l))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfDatalink(fd int) (int, error) {
|
||||
var t int
|
||||
err := ioctlPtr(fd, BIOCGDLT, unsafe.Pointer(&t))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfDatalink(fd, t int) (int, error) {
|
||||
err := ioctlPtr(fd, BIOCSDLT, unsafe.Pointer(&t))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfPromisc(fd, m int) error {
|
||||
err := ioctlPtr(fd, BIOCPROMISC, unsafe.Pointer(&m))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func FlushBpf(fd int) error {
|
||||
err := ioctlPtr(fd, BIOCFLUSH, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ivalue struct {
|
||||
name [IFNAMSIZ]byte
|
||||
value int16
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfInterface(fd int, name string) (string, error) {
|
||||
var iv ivalue
|
||||
err := ioctlPtr(fd, BIOCGETIF, unsafe.Pointer(&iv))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfInterface(fd int, name string) error {
|
||||
var iv ivalue
|
||||
copy(iv.name[:], []byte(name))
|
||||
err := ioctlPtr(fd, BIOCSETIF, unsafe.Pointer(&iv))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfTimeout(fd int) (*Timeval, error) {
|
||||
var tv Timeval
|
||||
err := ioctlPtr(fd, BIOCGRTIMEOUT, unsafe.Pointer(&tv))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tv, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfTimeout(fd int, tv *Timeval) error {
|
||||
err := ioctlPtr(fd, BIOCSRTIMEOUT, unsafe.Pointer(tv))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfStats(fd int) (*BpfStat, error) {
|
||||
var s BpfStat
|
||||
err := ioctlPtr(fd, BIOCGSTATS, unsafe.Pointer(&s))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &s, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfImmediate(fd, m int) error {
|
||||
err := ioctlPtr(fd, BIOCIMMEDIATE, unsafe.Pointer(&m))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpf(fd int, i []BpfInsn) error {
|
||||
var p BpfProgram
|
||||
p.Len = uint32(len(i))
|
||||
p.Insns = (*BpfInsn)(unsafe.Pointer(&i[0]))
|
||||
err := ioctlPtr(fd, BIOCSETF, unsafe.Pointer(&p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func CheckBpfVersion(fd int) error {
|
||||
var v BpfVersion
|
||||
err := ioctlPtr(fd, BIOCVERSION, unsafe.Pointer(&v))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v.Major != BPF_MAJOR_VERSION || v.Minor != BPF_MINOR_VERSION {
|
||||
return EINVAL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func BpfHeadercmpl(fd int) (int, error) {
|
||||
var f int
|
||||
err := ioctlPtr(fd, BIOCGHDRCMPLT, unsafe.Pointer(&f))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetBpfHeadercmpl(fd, f int) error {
|
||||
err := ioctlPtr(fd, BIOCSHDRCMPLT, unsafe.Pointer(&f))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
74
src/syscall/const_plan9.go
Normal file
74
src/syscall/const_plan9.go
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2014 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 syscall
|
||||
|
||||
// Plan 9 Constants
|
||||
|
||||
// Open modes
|
||||
const (
|
||||
O_RDONLY = 0
|
||||
O_WRONLY = 1
|
||||
O_RDWR = 2
|
||||
O_TRUNC = 16
|
||||
O_CLOEXEC = 32
|
||||
O_EXCL = 0x1000
|
||||
)
|
||||
|
||||
// Bind flags
|
||||
const (
|
||||
MORDER = 0x0003 // mask for bits defining order of mounting
|
||||
MREPL = 0x0000 // mount replaces object
|
||||
MBEFORE = 0x0001 // mount goes before others in union directory
|
||||
MAFTER = 0x0002 // mount goes after others in union directory
|
||||
MCREATE = 0x0004 // permit creation in mounted directory
|
||||
MCACHE = 0x0010 // cache some data
|
||||
MMASK = 0x0017 // all bits on
|
||||
)
|
||||
|
||||
// Rfork flags
|
||||
const (
|
||||
RFNAMEG = 1 << 0
|
||||
RFENVG = 1 << 1
|
||||
RFFDG = 1 << 2
|
||||
RFNOTEG = 1 << 3
|
||||
RFPROC = 1 << 4
|
||||
RFMEM = 1 << 5
|
||||
RFNOWAIT = 1 << 6
|
||||
RFCNAMEG = 1 << 10
|
||||
RFCENVG = 1 << 11
|
||||
RFCFDG = 1 << 12
|
||||
RFREND = 1 << 13
|
||||
RFNOMNT = 1 << 14
|
||||
)
|
||||
|
||||
// Qid.Type bits
|
||||
const (
|
||||
QTDIR = 0x80
|
||||
QTAPPEND = 0x40
|
||||
QTEXCL = 0x20
|
||||
QTMOUNT = 0x10
|
||||
QTAUTH = 0x08
|
||||
QTTMP = 0x04
|
||||
QTFILE = 0x00
|
||||
)
|
||||
|
||||
// Dir.Mode bits
|
||||
const (
|
||||
DMDIR = 0x80000000
|
||||
DMAPPEND = 0x40000000
|
||||
DMEXCL = 0x20000000
|
||||
DMMOUNT = 0x10000000
|
||||
DMAUTH = 0x08000000
|
||||
DMTMP = 0x04000000
|
||||
DMREAD = 0x4
|
||||
DMWRITE = 0x2
|
||||
DMEXEC = 0x1
|
||||
)
|
||||
|
||||
const (
|
||||
STATMAX = 65535
|
||||
ERRMAX = 128
|
||||
STATFIXLEN = 49
|
||||
)
|
||||
137
src/syscall/creds_test.go
Normal file
137
src/syscall/creds_test.go
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright 2012 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_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestSCMCredentials tests the sending and receiving of credentials
|
||||
// (PID, UID, GID) in an ancillary message between two UNIX
|
||||
// sockets. The SO_PASSCRED socket option is enabled on the sending
|
||||
// socket for this to work.
|
||||
func TestSCMCredentials(t *testing.T) {
|
||||
socketTypeTests := []struct {
|
||||
socketType int
|
||||
dataLen int
|
||||
}{
|
||||
{
|
||||
syscall.SOCK_STREAM,
|
||||
1,
|
||||
}, {
|
||||
syscall.SOCK_DGRAM,
|
||||
0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range socketTypeTests {
|
||||
fds, err := syscall.Socketpair(syscall.AF_LOCAL, tt.socketType, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Socketpair: %v", err)
|
||||
}
|
||||
|
||||
err = syscall.SetsockoptInt(fds[0], syscall.SOL_SOCKET, syscall.SO_PASSCRED, 1)
|
||||
if err != nil {
|
||||
syscall.Close(fds[0])
|
||||
syscall.Close(fds[1])
|
||||
t.Fatalf("SetsockoptInt: %v", err)
|
||||
}
|
||||
|
||||
srvFile := os.NewFile(uintptr(fds[0]), "server")
|
||||
cliFile := os.NewFile(uintptr(fds[1]), "client")
|
||||
defer srvFile.Close()
|
||||
defer cliFile.Close()
|
||||
|
||||
srv, err := net.FileConn(srvFile)
|
||||
if err != nil {
|
||||
t.Errorf("FileConn: %v", err)
|
||||
return
|
||||
}
|
||||
defer srv.Close()
|
||||
|
||||
cli, err := net.FileConn(cliFile)
|
||||
if err != nil {
|
||||
t.Errorf("FileConn: %v", err)
|
||||
return
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
var ucred syscall.Ucred
|
||||
if os.Getuid() != 0 {
|
||||
ucred.Pid = int32(os.Getpid())
|
||||
ucred.Uid = 0
|
||||
ucred.Gid = 0
|
||||
oob := syscall.UnixCredentials(&ucred)
|
||||
_, _, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
|
||||
if op, ok := err.(*net.OpError); ok {
|
||||
err = op.Err
|
||||
}
|
||||
if sys, ok := err.(*os.SyscallError); ok {
|
||||
err = sys.Err
|
||||
}
|
||||
switch err {
|
||||
case syscall.EPERM, syscall.EINVAL:
|
||||
default:
|
||||
t.Fatalf("WriteMsgUnix failed with %v, want EPERM or EINVAL", err)
|
||||
}
|
||||
}
|
||||
|
||||
ucred.Pid = int32(os.Getpid())
|
||||
ucred.Uid = uint32(os.Getuid())
|
||||
ucred.Gid = uint32(os.Getgid())
|
||||
oob := syscall.UnixCredentials(&ucred)
|
||||
|
||||
// On SOCK_STREAM, this is internally going to send a dummy byte
|
||||
n, oobn, err := cli.(*net.UnixConn).WriteMsgUnix(nil, oob, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteMsgUnix: %v", err)
|
||||
}
|
||||
if n != 0 {
|
||||
t.Fatalf("WriteMsgUnix n = %d, want 0", n)
|
||||
}
|
||||
if oobn != len(oob) {
|
||||
t.Fatalf("WriteMsgUnix oobn = %d, want %d", oobn, len(oob))
|
||||
}
|
||||
|
||||
oob2 := make([]byte, 10*len(oob))
|
||||
n, oobn2, flags, _, err := srv.(*net.UnixConn).ReadMsgUnix(nil, oob2)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadMsgUnix: %v", err)
|
||||
}
|
||||
if flags != syscall.MSG_CMSG_CLOEXEC {
|
||||
t.Fatalf("ReadMsgUnix flags = %#x, want %#x (MSG_CMSG_CLOEXEC)", flags, syscall.MSG_CMSG_CLOEXEC)
|
||||
}
|
||||
if n != tt.dataLen {
|
||||
t.Fatalf("ReadMsgUnix n = %d, want %d", n, tt.dataLen)
|
||||
}
|
||||
if oobn2 != oobn {
|
||||
// without SO_PASSCRED set on the socket, ReadMsgUnix will
|
||||
// return zero oob bytes
|
||||
t.Fatalf("ReadMsgUnix oobn = %d, want %d", oobn2, oobn)
|
||||
}
|
||||
oob2 = oob2[:oobn2]
|
||||
if !bytes.Equal(oob, oob2) {
|
||||
t.Fatal("ReadMsgUnix oob bytes don't match")
|
||||
}
|
||||
|
||||
scm, err := syscall.ParseSocketControlMessage(oob2)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseSocketControlMessage: %v", err)
|
||||
}
|
||||
newUcred, err := syscall.ParseUnixCredentials(&scm[0])
|
||||
if err != nil {
|
||||
t.Fatalf("ParseUnixCredentials: %v", err)
|
||||
}
|
||||
if *newUcred != ucred {
|
||||
t.Fatalf("ParseUnixCredentials = %+v, want %+v", newUcred, ucred)
|
||||
}
|
||||
}
|
||||
}
|
||||
204
src/syscall/dir_plan9.go
Normal file
204
src/syscall/dir_plan9.go
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
// Plan 9 directory marshaling. See intro(5).
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"internal/byteorder"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrShortStat = errors.New("stat buffer too short")
|
||||
ErrBadStat = errors.New("malformed stat buffer")
|
||||
ErrBadName = errors.New("bad character in file name")
|
||||
)
|
||||
|
||||
// A Qid represents a 9P server's unique identification for a file.
|
||||
type Qid struct {
|
||||
Path uint64 // the file server's unique identification for the file
|
||||
Vers uint32 // version number for given Path
|
||||
Type uint8 // the type of the file (syscall.QTDIR for example)
|
||||
}
|
||||
|
||||
// A Dir contains the metadata for a file.
|
||||
type Dir struct {
|
||||
// system-modified data
|
||||
Type uint16 // server type
|
||||
Dev uint32 // server subtype
|
||||
|
||||
// file data
|
||||
Qid Qid // unique id from server
|
||||
Mode uint32 // permissions
|
||||
Atime uint32 // last read time
|
||||
Mtime uint32 // last write time
|
||||
Length int64 // file length
|
||||
Name string // last element of path
|
||||
Uid string // owner name
|
||||
Gid string // group name
|
||||
Muid string // last modifier name
|
||||
}
|
||||
|
||||
var nullDir = Dir{
|
||||
Type: ^uint16(0),
|
||||
Dev: ^uint32(0),
|
||||
Qid: Qid{
|
||||
Path: ^uint64(0),
|
||||
Vers: ^uint32(0),
|
||||
Type: ^uint8(0),
|
||||
},
|
||||
Mode: ^uint32(0),
|
||||
Atime: ^uint32(0),
|
||||
Mtime: ^uint32(0),
|
||||
Length: ^int64(0),
|
||||
}
|
||||
|
||||
// Null assigns special "don't touch" values to members of d to
|
||||
// avoid modifying them during [Wstat].
|
||||
func (d *Dir) Null() { *d = nullDir }
|
||||
|
||||
// Marshal encodes a 9P stat message corresponding to d into b
|
||||
//
|
||||
// If there isn't enough space in b for a stat message, [ErrShortStat] is returned.
|
||||
func (d *Dir) Marshal(b []byte) (n int, err error) {
|
||||
n = STATFIXLEN + len(d.Name) + len(d.Uid) + len(d.Gid) + len(d.Muid)
|
||||
if n > len(b) {
|
||||
return n, ErrShortStat
|
||||
}
|
||||
|
||||
for _, c := range d.Name {
|
||||
if c == '/' {
|
||||
return n, ErrBadName
|
||||
}
|
||||
}
|
||||
|
||||
b = pbit16(b, uint16(n)-2)
|
||||
b = pbit16(b, d.Type)
|
||||
b = pbit32(b, d.Dev)
|
||||
b = pbit8(b, d.Qid.Type)
|
||||
b = pbit32(b, d.Qid.Vers)
|
||||
b = pbit64(b, d.Qid.Path)
|
||||
b = pbit32(b, d.Mode)
|
||||
b = pbit32(b, d.Atime)
|
||||
b = pbit32(b, d.Mtime)
|
||||
b = pbit64(b, uint64(d.Length))
|
||||
b = pstring(b, d.Name)
|
||||
b = pstring(b, d.Uid)
|
||||
b = pstring(b, d.Gid)
|
||||
b = pstring(b, d.Muid)
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// UnmarshalDir decodes a single 9P stat message from b and returns the resulting Dir.
|
||||
//
|
||||
// If b is too small to hold a valid stat message, [ErrShortStat] is returned.
|
||||
//
|
||||
// If the stat message itself is invalid, [ErrBadStat] is returned.
|
||||
func UnmarshalDir(b []byte) (*Dir, error) {
|
||||
if len(b) < STATFIXLEN {
|
||||
return nil, ErrShortStat
|
||||
}
|
||||
size, buf := gbit16(b)
|
||||
if len(b) != int(size)+2 {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
b = buf
|
||||
|
||||
var d Dir
|
||||
d.Type, b = gbit16(b)
|
||||
d.Dev, b = gbit32(b)
|
||||
d.Qid.Type, b = gbit8(b)
|
||||
d.Qid.Vers, b = gbit32(b)
|
||||
d.Qid.Path, b = gbit64(b)
|
||||
d.Mode, b = gbit32(b)
|
||||
d.Atime, b = gbit32(b)
|
||||
d.Mtime, b = gbit32(b)
|
||||
|
||||
n, b := gbit64(b)
|
||||
d.Length = int64(n)
|
||||
|
||||
var ok bool
|
||||
if d.Name, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Uid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Gid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
if d.Muid, b, ok = gstring(b); !ok {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
}
|
||||
|
||||
// pbit8 copies the 8-bit number v to b and returns the remaining slice of b.
|
||||
func pbit8(b []byte, v uint8) []byte {
|
||||
b[0] = byte(v)
|
||||
return b[1:]
|
||||
}
|
||||
|
||||
// pbit16 copies the 16-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit16(b []byte, v uint16) []byte {
|
||||
byteorder.LePutUint16(b, v)
|
||||
return b[2:]
|
||||
}
|
||||
|
||||
// pbit32 copies the 32-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit32(b []byte, v uint32) []byte {
|
||||
byteorder.LePutUint32(b, v)
|
||||
return b[4:]
|
||||
}
|
||||
|
||||
// pbit64 copies the 64-bit number v to b in little-endian order and returns the remaining slice of b.
|
||||
func pbit64(b []byte, v uint64) []byte {
|
||||
byteorder.LePutUint64(b, v)
|
||||
return b[8:]
|
||||
}
|
||||
|
||||
// pstring copies the string s to b, prepending it with a 16-bit length in little-endian order, and
|
||||
// returning the remaining slice of b..
|
||||
func pstring(b []byte, s string) []byte {
|
||||
b = pbit16(b, uint16(len(s)))
|
||||
n := copy(b, s)
|
||||
return b[n:]
|
||||
}
|
||||
|
||||
// gbit8 reads an 8-bit number from b and returns it with the remaining slice of b.
|
||||
func gbit8(b []byte) (uint8, []byte) {
|
||||
return uint8(b[0]), b[1:]
|
||||
}
|
||||
|
||||
// gbit16 reads a 16-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
//
|
||||
//go:nosplit
|
||||
func gbit16(b []byte) (uint16, []byte) {
|
||||
return byteorder.LeUint16(b), b[2:]
|
||||
}
|
||||
|
||||
// gbit32 reads a 32-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit32(b []byte) (uint32, []byte) {
|
||||
return byteorder.LeUint32(b), b[4:]
|
||||
}
|
||||
|
||||
// gbit64 reads a 64-bit number in little-endian order from b and returns it with the remaining slice of b.
|
||||
func gbit64(b []byte) (uint64, []byte) {
|
||||
return byteorder.LeUint64(b), b[8:]
|
||||
}
|
||||
|
||||
// gstring reads a string from b, prefixed with a 16-bit length in little-endian order.
|
||||
// It returns the string with the remaining slice of b and a boolean. If the length is
|
||||
// greater than the number of bytes in b, the boolean will be false.
|
||||
func gstring(b []byte) (string, []byte, bool) {
|
||||
n, b := gbit16(b)
|
||||
if int(n) > len(b) {
|
||||
return "", b, false
|
||||
}
|
||||
return string(b[:n]), b[n:], true
|
||||
}
|
||||
101
src/syscall/dirent.go
Normal file
101
src/syscall/dirent.go
Normal file
@@ -0,0 +1,101 @@
|
||||
// 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 || (js && wasm) || wasip1
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"internal/byteorder"
|
||||
"internal/goarch"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// readInt returns the size-bytes unsigned integer in native byte order at offset off.
|
||||
func readInt(b []byte, off, size uintptr) (u uint64, ok bool) {
|
||||
if len(b) < int(off+size) {
|
||||
return 0, false
|
||||
}
|
||||
if goarch.BigEndian {
|
||||
return readIntBE(b[off:], size), true
|
||||
}
|
||||
return readIntLE(b[off:], size), true
|
||||
}
|
||||
|
||||
func readIntBE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
return uint64(byteorder.BeUint16(b))
|
||||
case 4:
|
||||
return uint64(byteorder.BeUint32(b))
|
||||
case 8:
|
||||
return uint64(byteorder.BeUint64(b))
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
|
||||
func readIntLE(b []byte, size uintptr) uint64 {
|
||||
switch size {
|
||||
case 1:
|
||||
return uint64(b[0])
|
||||
case 2:
|
||||
return uint64(byteorder.LeUint16(b))
|
||||
case 4:
|
||||
return uint64(byteorder.LeUint32(b))
|
||||
case 8:
|
||||
return uint64(byteorder.LeUint64(b))
|
||||
default:
|
||||
panic("syscall: readInt with unsupported size")
|
||||
}
|
||||
}
|
||||
|
||||
// ParseDirent parses up to max directory entries in buf,
|
||||
// appending the names to names. It returns the number of
|
||||
// bytes consumed from buf, the number of entries added
|
||||
// to names, and the new names slice.
|
||||
func ParseDirent(buf []byte, max int, names []string) (consumed int, count int, newnames []string) {
|
||||
origlen := len(buf)
|
||||
count = 0
|
||||
for max != 0 && len(buf) > 0 {
|
||||
reclen, ok := direntReclen(buf)
|
||||
if !ok || reclen > uint64(len(buf)) {
|
||||
return origlen, count, names
|
||||
}
|
||||
rec := buf[:reclen]
|
||||
buf = buf[reclen:]
|
||||
ino, ok := direntIno(rec)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
// See src/os/dir_unix.go for the reason why this condition is
|
||||
// excluded on wasip1.
|
||||
if ino == 0 && runtime.GOOS != "wasip1" { // File absent in directory.
|
||||
continue
|
||||
}
|
||||
const namoff = uint64(unsafe.Offsetof(Dirent{}.Name))
|
||||
namlen, ok := direntNamlen(rec)
|
||||
if !ok || namoff+namlen > uint64(len(rec)) {
|
||||
break
|
||||
}
|
||||
name := rec[namoff : namoff+namlen]
|
||||
for i, c := range name {
|
||||
if c == 0 {
|
||||
name = name[:i]
|
||||
break
|
||||
}
|
||||
}
|
||||
// Check for useless names before allocating a string.
|
||||
if string(name) == "." || string(name) == ".." {
|
||||
continue
|
||||
}
|
||||
max--
|
||||
count++
|
||||
names = append(names, string(name))
|
||||
}
|
||||
return origlen - len(buf), count, names
|
||||
}
|
||||
146
src/syscall/dirent_test.go
Normal file
146
src/syscall/dirent_test.go
Normal file
@@ -0,0 +1,146 @@
|
||||
// Copyright 2018 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_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestDirent(t *testing.T) {
|
||||
const (
|
||||
direntBufSize = 2048 // arbitrary? See https://go.dev/issue/37323.
|
||||
filenameMinSize = 11
|
||||
)
|
||||
|
||||
d := t.TempDir()
|
||||
t.Logf("tmpdir: %s", d)
|
||||
|
||||
for i, c := range []byte("0123456789") {
|
||||
name := string(bytes.Repeat([]byte{c}, filenameMinSize+i))
|
||||
err := os.WriteFile(filepath.Join(d, name), nil, 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("writefile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
names := make([]string, 0, 10)
|
||||
|
||||
fd, err := syscall.Open(d, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("syscall.open: %v", err)
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
|
||||
buf := bytes.Repeat([]byte{0xCD}, direntBufSize)
|
||||
for {
|
||||
n, err := syscall.ReadDirent(fd, buf)
|
||||
if err == syscall.EINVAL {
|
||||
// On linux, 'man getdents64' says that EINVAL indicates “result buffer is too small”.
|
||||
// Try a bigger buffer.
|
||||
t.Logf("ReadDirent: %v; retrying with larger buffer", err)
|
||||
buf = bytes.Repeat([]byte{0xCD}, len(buf)*2)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("syscall.readdir: %v", err)
|
||||
}
|
||||
t.Logf("ReadDirent: read %d bytes", n)
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
var consumed, count int
|
||||
consumed, count, names = syscall.ParseDirent(buf[:n], -1, names)
|
||||
t.Logf("ParseDirent: %d new name(s)", count)
|
||||
if consumed != n {
|
||||
t.Fatalf("ParseDirent: consumed %d bytes; expected %d", consumed, n)
|
||||
}
|
||||
}
|
||||
|
||||
slices.Sort(names)
|
||||
t.Logf("names: %q", names)
|
||||
|
||||
if len(names) != 10 {
|
||||
t.Errorf("got %d names; expected 10", len(names))
|
||||
}
|
||||
for i, name := range names {
|
||||
ord, err := strconv.Atoi(name[:1])
|
||||
if err != nil {
|
||||
t.Fatalf("names[%d] is non-integer %q: %v", i, names[i], err)
|
||||
}
|
||||
if expected := strings.Repeat(name[:1], filenameMinSize+ord); name != expected {
|
||||
t.Errorf("names[%d] is %q (len %d); expected %q (len %d)", i, name, len(name), expected, len(expected))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDirentRepeat(t *testing.T) {
|
||||
const N = 100
|
||||
// Note: the size of the buffer is small enough that the loop
|
||||
// below will need to execute multiple times. See issue #31368.
|
||||
size := N * unsafe.Offsetof(syscall.Dirent{}.Name) / 4
|
||||
if runtime.GOOS == "freebsd" || runtime.GOOS == "netbsd" {
|
||||
if size < 1024 {
|
||||
size = 1024 // DIRBLKSIZ, see issue 31403.
|
||||
}
|
||||
}
|
||||
|
||||
// Make a directory containing N files
|
||||
d := t.TempDir()
|
||||
|
||||
var files []string
|
||||
for i := 0; i < N; i++ {
|
||||
files = append(files, fmt.Sprintf("file%d", i))
|
||||
}
|
||||
for _, file := range files {
|
||||
err := os.WriteFile(filepath.Join(d, file), []byte("contents"), 0644)
|
||||
if err != nil {
|
||||
t.Fatalf("writefile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Read the directory entries using ReadDirent.
|
||||
fd, err := syscall.Open(d, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("syscall.open: %v", err)
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
var files2 []string
|
||||
for {
|
||||
buf := make([]byte, size)
|
||||
n, err := syscall.ReadDirent(fd, buf)
|
||||
if err != nil {
|
||||
t.Fatalf("syscall.readdir: %v", err)
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
buf = buf[:n]
|
||||
for len(buf) > 0 {
|
||||
var consumed int
|
||||
consumed, _, files2 = syscall.ParseDirent(buf, -1, files2)
|
||||
buf = buf[consumed:]
|
||||
}
|
||||
}
|
||||
|
||||
// Check results
|
||||
slices.Sort(files)
|
||||
slices.Sort(files2)
|
||||
if strings.Join(files, "|") != strings.Join(files2, "|") {
|
||||
t.Errorf("bad file list: want\n%q\ngot\n%q", files, files2)
|
||||
}
|
||||
}
|
||||
287
src/syscall/dll_windows.go
Normal file
287
src/syscall/dll_windows.go
Normal file
@@ -0,0 +1,287 @@
|
||||
// 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.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"internal/syscall/windows/sysdll"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// DLLError describes reasons for DLL load failures.
|
||||
type DLLError struct {
|
||||
Err error
|
||||
ObjName string
|
||||
Msg string
|
||||
}
|
||||
|
||||
func (e *DLLError) Error() string { return e.Msg }
|
||||
|
||||
func (e *DLLError) Unwrap() error { return e.Err }
|
||||
|
||||
// Implemented in ../runtime/syscall_windows.go.
|
||||
|
||||
// Deprecated: Use [SyscallN] instead.
|
||||
func Syscall(trap, nargs, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
// Deprecated: Use [SyscallN] instead.
|
||||
func Syscall6(trap, nargs, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
// Deprecated: Use [SyscallN] instead.
|
||||
func Syscall9(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
// Deprecated: Use [SyscallN] instead.
|
||||
func Syscall12(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
// Deprecated: Use [SyscallN] instead.
|
||||
func Syscall15(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
// Deprecated: Use [SyscallN] instead.
|
||||
func Syscall18(trap, nargs, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 uintptr) (r1, r2 uintptr, err Errno)
|
||||
|
||||
func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)
|
||||
func loadlibrary(filename *uint16) (handle uintptr, err Errno)
|
||||
func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)
|
||||
func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)
|
||||
|
||||
// A DLL implements access to a single DLL.
|
||||
type DLL struct {
|
||||
Name string
|
||||
Handle Handle
|
||||
}
|
||||
|
||||
// LoadDLL loads the named DLL file into memory.
|
||||
//
|
||||
// If name is not an absolute path and is not a known system DLL used by
|
||||
// Go, Windows will search for the named DLL in many locations, causing
|
||||
// potential DLL preloading attacks.
|
||||
//
|
||||
// Use [LazyDLL] in golang.org/x/sys/windows for a secure way to
|
||||
// load system DLLs.
|
||||
func LoadDLL(name string) (*DLL, error) {
|
||||
namep, err := UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var h uintptr
|
||||
var e Errno
|
||||
if sysdll.IsSystemDLL[name] {
|
||||
h, e = loadsystemlibrary(namep)
|
||||
} else {
|
||||
h, e = loadlibrary(namep)
|
||||
}
|
||||
if e != 0 {
|
||||
return nil, &DLLError{
|
||||
Err: e,
|
||||
ObjName: name,
|
||||
Msg: "Failed to load " + name + ": " + e.Error(),
|
||||
}
|
||||
}
|
||||
d := &DLL{
|
||||
Name: name,
|
||||
Handle: Handle(h),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// MustLoadDLL is like [LoadDLL] but panics if load operation fails.
|
||||
func MustLoadDLL(name string) *DLL {
|
||||
d, e := LoadDLL(name)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// FindProc searches [DLL] d for procedure named name and returns [*Proc]
|
||||
// if found. It returns an error if search fails.
|
||||
func (d *DLL) FindProc(name string) (proc *Proc, err error) {
|
||||
namep, err := BytePtrFromString(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a, e := getprocaddress(uintptr(d.Handle), namep)
|
||||
if e != 0 {
|
||||
return nil, &DLLError{
|
||||
Err: e,
|
||||
ObjName: name,
|
||||
Msg: "Failed to find " + name + " procedure in " + d.Name + ": " + e.Error(),
|
||||
}
|
||||
}
|
||||
p := &Proc{
|
||||
Dll: d,
|
||||
Name: name,
|
||||
addr: a,
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
// MustFindProc is like [DLL.FindProc] but panics if search fails.
|
||||
func (d *DLL) MustFindProc(name string) *Proc {
|
||||
p, e := d.FindProc(name)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
// Release unloads [DLL] d from memory.
|
||||
func (d *DLL) Release() (err error) {
|
||||
return FreeLibrary(d.Handle)
|
||||
}
|
||||
|
||||
// A Proc implements access to a procedure inside a [DLL].
|
||||
type Proc struct {
|
||||
Dll *DLL
|
||||
Name string
|
||||
addr uintptr
|
||||
}
|
||||
|
||||
// Addr returns the address of the procedure represented by p.
|
||||
// The return value can be passed to Syscall to run the procedure.
|
||||
func (p *Proc) Addr() uintptr {
|
||||
return p.addr
|
||||
}
|
||||
|
||||
// Call executes procedure p with arguments a.
|
||||
//
|
||||
// The returned error is always non-nil, constructed from the result of GetLastError.
|
||||
// Callers must inspect the primary return value to decide whether an error occurred
|
||||
// (according to the semantics of the specific function being called) before consulting
|
||||
// the error. The error always has type [Errno].
|
||||
//
|
||||
// On amd64, Call can pass and return floating-point values. To pass
|
||||
// an argument x with C type "float", use
|
||||
// uintptr(math.Float32bits(x)). To pass an argument with C type
|
||||
// "double", use uintptr(math.Float64bits(x)). Floating-point return
|
||||
// values are returned in r2. The return value for C type "float" is
|
||||
// [math.Float32frombits](uint32(r2)). For C type "double", it is
|
||||
// [math.Float64frombits](uint64(r2)).
|
||||
//
|
||||
//go:uintptrescapes
|
||||
func (p *Proc) Call(a ...uintptr) (uintptr, uintptr, error) {
|
||||
return SyscallN(p.Addr(), a...)
|
||||
}
|
||||
|
||||
// A LazyDLL implements access to a single [DLL].
|
||||
// It will delay the load of the DLL until the first
|
||||
// call to its [LazyDLL.Handle] method or to one of its
|
||||
// [LazyProc]'s Addr method.
|
||||
//
|
||||
// LazyDLL is subject to the same DLL preloading attacks as documented
|
||||
// on [LoadDLL].
|
||||
//
|
||||
// Use LazyDLL in golang.org/x/sys/windows for a secure way to
|
||||
// load system DLLs.
|
||||
type LazyDLL struct {
|
||||
mu sync.Mutex
|
||||
dll *DLL // non nil once DLL is loaded
|
||||
Name string
|
||||
}
|
||||
|
||||
// Load loads DLL file d.Name into memory. It returns an error if fails.
|
||||
// Load will not try to load DLL, if it is already loaded into memory.
|
||||
func (d *LazyDLL) Load() error {
|
||||
// Non-racy version of:
|
||||
// if d.dll == nil {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll))) == nil {
|
||||
d.mu.Lock()
|
||||
defer d.mu.Unlock()
|
||||
if d.dll == nil {
|
||||
dll, e := LoadDLL(d.Name)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
// Non-racy version of:
|
||||
// d.dll = dll
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&d.dll)), unsafe.Pointer(dll))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mustLoad is like Load but panics if search fails.
|
||||
func (d *LazyDLL) mustLoad() {
|
||||
e := d.Load()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Handle returns d's module handle.
|
||||
func (d *LazyDLL) Handle() uintptr {
|
||||
d.mustLoad()
|
||||
return uintptr(d.dll.Handle)
|
||||
}
|
||||
|
||||
// NewProc returns a [LazyProc] for accessing the named procedure in the [DLL] d.
|
||||
func (d *LazyDLL) NewProc(name string) *LazyProc {
|
||||
return &LazyProc{l: d, Name: name}
|
||||
}
|
||||
|
||||
// NewLazyDLL creates new [LazyDLL] associated with [DLL] file.
|
||||
func NewLazyDLL(name string) *LazyDLL {
|
||||
return &LazyDLL{Name: name}
|
||||
}
|
||||
|
||||
// A LazyProc implements access to a procedure inside a [LazyDLL].
|
||||
// It delays the lookup until the [LazyProc.Addr], [LazyProc.Call], or [LazyProc.Find] method is called.
|
||||
type LazyProc struct {
|
||||
mu sync.Mutex
|
||||
Name string
|
||||
l *LazyDLL
|
||||
proc *Proc
|
||||
}
|
||||
|
||||
// Find searches [DLL] for procedure named p.Name. It returns
|
||||
// an error if search fails. Find will not search procedure,
|
||||
// if it is already found and loaded into memory.
|
||||
func (p *LazyProc) Find() error {
|
||||
// Non-racy version of:
|
||||
// if p.proc == nil {
|
||||
if atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc))) == nil {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.proc == nil {
|
||||
e := p.l.Load()
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
proc, e := p.l.dll.FindProc(p.Name)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
// Non-racy version of:
|
||||
// p.proc = proc
|
||||
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(&p.proc)), unsafe.Pointer(proc))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// mustFind is like Find but panics if search fails.
|
||||
func (p *LazyProc) mustFind() {
|
||||
e := p.Find()
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
// Addr returns the address of the procedure represented by p.
|
||||
// The return value can be passed to Syscall to run the procedure.
|
||||
func (p *LazyProc) Addr() uintptr {
|
||||
p.mustFind()
|
||||
return p.proc.Addr()
|
||||
}
|
||||
|
||||
// Call executes procedure p with arguments a. See the documentation of
|
||||
// Proc.Call for more information.
|
||||
//
|
||||
//go:uintptrescapes
|
||||
func (p *LazyProc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error) {
|
||||
p.mustFind()
|
||||
return p.proc.Call(a...)
|
||||
}
|
||||
150
src/syscall/env_unix.go
Normal file
150
src/syscall/env_unix.go
Normal file
@@ -0,0 +1,150 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
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 = runtime_envs()
|
||||
)
|
||||
|
||||
func runtime_envs() []string // in package runtime
|
||||
|
||||
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)
|
||||
}
|
||||
runtimeUnsetenv(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
|
||||
}
|
||||
|
||||
func Setenv(key, value string) error {
|
||||
envOnce.Do(copyenv)
|
||||
if len(key) == 0 {
|
||||
return EINVAL
|
||||
}
|
||||
for i := 0; i < len(key); i++ {
|
||||
if key[i] == '=' || key[i] == 0 {
|
||||
return 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 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
|
||||
runtimeSetenv(key, value)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Clearenv() {
|
||||
envOnce.Do(copyenv) // prevent copyenv in Getenv/Setenv
|
||||
|
||||
envLock.Lock()
|
||||
defer envLock.Unlock()
|
||||
|
||||
for k := range env {
|
||||
runtimeUnsetenv(k)
|
||||
}
|
||||
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
|
||||
}
|
||||
96
src/syscall/env_windows.go
Normal file
96
src/syscall/env_windows.go
Normal 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
|
||||
}
|
||||
59
src/syscall/errors_plan9.go
Normal file
59
src/syscall/errors_plan9.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
package syscall
|
||||
|
||||
// Constants
|
||||
const (
|
||||
// Invented values to support what package os expects.
|
||||
O_CREAT = 0x02000
|
||||
O_APPEND = 0x00400
|
||||
O_NOCTTY = 0x00000
|
||||
O_NONBLOCK = 0x00000
|
||||
O_SYNC = 0x00000
|
||||
O_ASYNC = 0x00000
|
||||
|
||||
S_IFMT = 0x1f000
|
||||
S_IFIFO = 0x1000
|
||||
S_IFCHR = 0x2000
|
||||
S_IFDIR = 0x4000
|
||||
S_IFBLK = 0x6000
|
||||
S_IFREG = 0x8000
|
||||
S_IFLNK = 0xa000
|
||||
S_IFSOCK = 0xc000
|
||||
)
|
||||
|
||||
// Errors
|
||||
var (
|
||||
EINVAL = NewError("bad arg in system call")
|
||||
ENOTDIR = NewError("not a directory")
|
||||
EISDIR = NewError("file is a directory")
|
||||
ENOENT = NewError("file does not exist")
|
||||
EEXIST = NewError("file already exists")
|
||||
EMFILE = NewError("no free file descriptors")
|
||||
EIO = NewError("i/o error")
|
||||
ENAMETOOLONG = NewError("file name too long")
|
||||
EINTR = NewError("interrupted")
|
||||
EPERM = NewError("permission denied")
|
||||
EBUSY = NewError("no free devices")
|
||||
ETIMEDOUT = NewError("connection timed out")
|
||||
EPLAN9 = NewError("not supported by plan 9")
|
||||
|
||||
// The following errors do not correspond to any
|
||||
// Plan 9 system messages. Invented to support
|
||||
// what package os and others expect.
|
||||
EACCES = NewError("access permission denied")
|
||||
EAFNOSUPPORT = NewError("address family not supported by protocol")
|
||||
ESPIPE = NewError("illegal seek")
|
||||
)
|
||||
|
||||
// Notes
|
||||
const (
|
||||
SIGABRT = Note("abort")
|
||||
SIGALRM = Note("alarm")
|
||||
SIGHUP = Note("hangup")
|
||||
SIGINT = Note("interrupt")
|
||||
SIGKILL = Note("kill")
|
||||
SIGTERM = Note("interrupt")
|
||||
)
|
||||
46
src/syscall/exec_aix_test.go
Normal file
46
src/syscall/exec_aix_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2015 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
|
||||
|
||||
package syscall
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:cgo_import_dynamic libc_Getpgid getpgid "libc.a/shr_64.o"
|
||||
//go:cgo_import_dynamic libc_Getpgrp getpgrp "libc.a/shr_64.o"
|
||||
|
||||
//go:linkname libc_Getpgid libc_Getpgid
|
||||
//go:linkname libc_Getpgrp libc_Getpgrp
|
||||
|
||||
var (
|
||||
libc_Getpgid,
|
||||
libc_Getpgrp libcFunc
|
||||
)
|
||||
|
||||
func Getpgid(pid int) (pgid int, err error) {
|
||||
r0, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_Getpgid)), 1, uintptr(pid), 0, 0, 0, 0, 0)
|
||||
pgid = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Getpgrp() (pgrp int) {
|
||||
r0, _, _ := syscall6(uintptr(unsafe.Pointer(&libc_Getpgrp)), 0, 0, 0, 0, 0, 0, 0)
|
||||
pgrp = int(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func Tcgetpgrp(fd int) (pgid int32, err error) {
|
||||
if errno := ioctlPtr(uintptr(fd), TIOCGPGRP, unsafe.Pointer(&pgid)); errno != 0 {
|
||||
return -1, errno
|
||||
}
|
||||
return pgid, nil
|
||||
}
|
||||
|
||||
func Tcsetpgrp(fd int, pgid int32) (err error) {
|
||||
return ioctlPtr(uintptr(fd), TIOCSPGRP, unsafe.Pointer(&pgid))
|
||||
}
|
||||
300
src/syscall/exec_bsd.go
Normal file
300
src/syscall/exec_bsd.go
Normal file
@@ -0,0 +1,300 @@
|
||||
// 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 dragonfly || netbsd || (openbsd && mips64)
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, 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 _C_int
|
||||
cred *Credential
|
||||
ngroups, groups uintptr
|
||||
)
|
||||
|
||||
rlim := origRlimitNofile.Load()
|
||||
|
||||
// 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 = RawSyscall(SYS_FORK, 0, 0, 0)
|
||||
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.
|
||||
|
||||
// Enable tracing if requested.
|
||||
if sys.Ptrace {
|
||||
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
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 {
|
||||
// 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(SYS_GETPID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
pgrp = _C_int(r1)
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// 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 !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
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
_, _, err1 = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 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 {
|
||||
if runtime.GOOS == "netbsd" || (runtime.GOOS == "openbsd" && runtime.GOARCH == "mips64") {
|
||||
_, _, err1 = RawSyscall(_SYS_DUP3, uintptr(pipe), uintptr(nextfd), O_CLOEXEC)
|
||||
} else if runtime.GOOS == "dragonfly" {
|
||||
_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(pipe), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
|
||||
} else {
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(pipe), uintptr(nextfd), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_FCNTL, 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++
|
||||
}
|
||||
if runtime.GOOS == "netbsd" || (runtime.GOOS == "openbsd" && runtime.GOARCH == "mips64") {
|
||||
_, _, err1 = RawSyscall(_SYS_DUP3, uintptr(fd[i]), uintptr(nextfd), O_CLOEXEC)
|
||||
} else if runtime.GOOS == "dragonfly" {
|
||||
_, _, err1 = RawSyscall(SYS_FCNTL, uintptr(fd[i]), _F_DUP2FD_CLOEXEC, uintptr(nextfd))
|
||||
} else {
|
||||
_, _, err1 = RawSyscall(SYS_DUP2, uintptr(fd[i]), uintptr(nextfd), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
_, _, err1 = RawSyscall(SYS_FCNTL, 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 {
|
||||
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(SYS_FCNTL, 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_DUP2, 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), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original rlimit.
|
||||
if rlim != nil {
|
||||
RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(rlim)), 0)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
324
src/syscall/exec_freebsd.go
Normal file
324
src/syscall/exec_freebsd.go
Normal file
@@ -0,0 +1,324 @@
|
||||
// Copyright 2021 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 syscall
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
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.
|
||||
Pdeathsig Signal // Signal that the process will get when its parent dies (Linux and FreeBSD only)
|
||||
Jail int // Jail to which the child process is attached (FreeBSD only).
|
||||
}
|
||||
|
||||
const (
|
||||
_P_PID = 0
|
||||
|
||||
_PROC_PDEATHSIG_CTL = 11
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, 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 _C_int
|
||||
cred *Credential
|
||||
ngroups, groups uintptr
|
||||
upid uintptr
|
||||
)
|
||||
|
||||
rlim := origRlimitNofile.Load()
|
||||
|
||||
// Record parent PID so child can test if it has died.
|
||||
ppid, _, _ := RawSyscall(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++
|
||||
|
||||
// About to call fork.
|
||||
// No more allocation or calls of non-assembly functions.
|
||||
runtime_BeforeFork()
|
||||
r1, _, err1 = RawSyscall(SYS_FORK, 0, 0, 0)
|
||||
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.
|
||||
|
||||
// Attach to the given jail, if any. The system call also changes the
|
||||
// process' root and working directories to the jail's path directory.
|
||||
if sys.Jail > 0 {
|
||||
_, _, err1 = RawSyscall(SYS_JAIL_ATTACH, uintptr(sys.Jail), 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Enable tracing if requested.
|
||||
if sys.Ptrace {
|
||||
_, _, err1 = RawSyscall(SYS_PTRACE, uintptr(PTRACE_TRACEME), 0, 0)
|
||||
if err1 != 0 {
|
||||
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 {
|
||||
// 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(SYS_GETPID, 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
pgrp = _C_int(r1)
|
||||
}
|
||||
|
||||
// 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()
|
||||
|
||||
// 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 !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
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
switch runtime.GOARCH {
|
||||
case "386", "arm":
|
||||
_, _, err1 = RawSyscall6(SYS_PROCCTL, _P_PID, 0, 0, _PROC_PDEATHSIG_CTL, uintptr(unsafe.Pointer(&sys.Pdeathsig)), 0)
|
||||
default:
|
||||
_, _, err1 = RawSyscall6(SYS_PROCCTL, _P_PID, 0, _PROC_PDEATHSIG_CTL, uintptr(unsafe.Pointer(&sys.Pdeathsig)), 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.
|
||||
r1, _, _ = RawSyscall(SYS_GETPPID, 0, 0, 0)
|
||||
if r1 != ppid {
|
||||
upid, _, _ = RawSyscall(SYS_GETPID, 0, 0, 0)
|
||||
_, _, err1 = RawSyscall(SYS_KILL, upid, 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_FCNTL, uintptr(pipe), F_DUP2FD_CLOEXEC, uintptr(nextfd))
|
||||
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_FCNTL, uintptr(fd[i]), F_DUP2FD_CLOEXEC, uintptr(nextfd))
|
||||
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(SYS_FCNTL, 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_DUP2, 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), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original rlimit.
|
||||
if rlim != nil {
|
||||
RawSyscall(SYS_SETRLIMIT, uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(rlim)), 0)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
98
src/syscall/exec_freebsd_test.go
Normal file
98
src/syscall/exec_freebsd_test.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2023 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 freebsd
|
||||
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
flagJailCreate = uintptr(0x1)
|
||||
)
|
||||
|
||||
func prepareJail(t *testing.T) (int, string) {
|
||||
t.Helper()
|
||||
|
||||
root := t.TempDir()
|
||||
paramPath := []byte("path\x00")
|
||||
conf := make([]syscall.Iovec, 4)
|
||||
conf[0].Base = ¶mPath[0]
|
||||
conf[0].SetLen(len(paramPath))
|
||||
p, err := syscall.BytePtrFromString(root)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
conf[1].Base = p
|
||||
conf[1].SetLen(len(root) + 1)
|
||||
|
||||
paramPersist := []byte("persist\x00")
|
||||
conf[2].Base = ¶mPersist[0]
|
||||
conf[2].SetLen(len(paramPersist))
|
||||
conf[3].Base = nil
|
||||
conf[3].SetLen(0)
|
||||
|
||||
id, _, err1 := syscall.Syscall(syscall.SYS_JAIL_SET,
|
||||
uintptr(unsafe.Pointer(&conf[0])), uintptr(len(conf)), flagJailCreate)
|
||||
if err1 != 0 {
|
||||
t.Fatalf("jail_set: %v", err1)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_, _, err1 := syscall.Syscall(syscall.SYS_JAIL_REMOVE, id, 0, 0)
|
||||
if err1 != 0 {
|
||||
t.Errorf("failed to cleanup jail: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
return int(id), root
|
||||
}
|
||||
|
||||
func TestJailAttach(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
jailed, err := syscall.SysctlUint32("security.jail.jailed")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if jailed != 1 {
|
||||
t.Fatalf("jailed = %d, want 1", jailed)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
testenv.MustHaveGoBuild(t)
|
||||
// Make sure we are running as root, so we have permissions to create
|
||||
// and remove jails.
|
||||
if os.Getuid() != 0 {
|
||||
t.Skip("kernel prohibits jail system calls in unprivileged process")
|
||||
}
|
||||
|
||||
jid, root := prepareJail(t)
|
||||
|
||||
// Since jail attach does an implicit chroot to the jail's path,
|
||||
// we need the binary there, and it must be statically linked.
|
||||
x := filepath.Join(root, "syscall.test")
|
||||
cmd := exec.Command(testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
|
||||
cmd.Env = append(os.Environ(), "CGO_ENABLED=0")
|
||||
if o, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("Build of syscall in jail root failed, output %v, err %v", o, err)
|
||||
}
|
||||
|
||||
cmd = exec.Command("/syscall.test", "-test.run=TestJailAttach", "/")
|
||||
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Jail: jid}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
}
|
||||
324
src/syscall/exec_libc.go
Normal file
324
src/syscall/exec_libc.go
Normal file
@@ -0,0 +1,324 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
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.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, 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 := origRlimitNofile.Load()
|
||||
|
||||
// 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 rlim != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
func ioctlPtr(fd, req uintptr, arg unsafe.Pointer) (err Errno) {
|
||||
return ioctl(fd, req, uintptr(arg))
|
||||
}
|
||||
296
src/syscall/exec_libc2.go
Normal file
296
src/syscall/exec_libc2.go
Normal file
@@ -0,0 +1,296 @@
|
||||
// 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 (
|
||||
"internal/abi"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
// 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.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, 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
|
||||
err error
|
||||
pgrp _C_int
|
||||
cred *Credential
|
||||
ngroups, groups uintptr
|
||||
)
|
||||
|
||||
rlim := origRlimitNofile.Load()
|
||||
|
||||
// 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 = rawSyscall(abi.FuncPCABI0(libc_fork_trampoline), 0, 0, 0)
|
||||
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.
|
||||
|
||||
// Enable tracing if requested.
|
||||
if sys.Ptrace {
|
||||
if err = ptrace(PTRACE_TRACEME, 0, 0, 0); err != nil {
|
||||
err1 = err.(Errno)
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Session ID
|
||||
if sys.Setsid {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setsid_trampoline), 0, 0, 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set process group
|
||||
if sys.Setpgid || sys.Foreground {
|
||||
// Place child in process group.
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_setpgid_trampoline), 0, uintptr(sys.Pgid), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
if sys.Foreground {
|
||||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
// 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 = rawSyscall(abi.FuncPCABI0(libc_chroot_trampoline), 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 !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
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_chdir_trampoline), uintptr(unsafe.Pointer(dir)), 0, 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 {
|
||||
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++
|
||||
}
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] >= 0 && fd[i] < i {
|
||||
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++
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 2: dup fd[i] down onto i.
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] == -1 {
|
||||
rawSyscall(abi.FuncPCABI0(libc_close_trampoline), 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(abi.FuncPCABI0(libc_fcntl_trampoline), 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(abi.FuncPCABI0(libc_dup2_trampoline), 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(abi.FuncPCABI0(libc_close_trampoline), uintptr(i), 0, 0)
|
||||
}
|
||||
|
||||
// Detach fd 0 from tty
|
||||
if sys.Noctty {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), 0, uintptr(TIOCNOTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Set the controlling TTY to Ctty
|
||||
if sys.Setctty {
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_ioctl_trampoline), uintptr(sys.Ctty), uintptr(TIOCSCTTY), 0)
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Restore original rlimit.
|
||||
if rlim != nil {
|
||||
rawSyscall(abi.FuncPCABI0(libc_setrlimit_trampoline), uintptr(RLIMIT_NOFILE), uintptr(unsafe.Pointer(rlim)), 0)
|
||||
}
|
||||
|
||||
// Time to exec.
|
||||
_, _, err1 = rawSyscall(abi.FuncPCABI0(libc_execve_trampoline),
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])),
|
||||
uintptr(unsafe.Pointer(&envv[0])))
|
||||
|
||||
childerror:
|
||||
// send error code on pipe
|
||||
rawSyscall(abi.FuncPCABI0(libc_write_trampoline), uintptr(pipe), uintptr(unsafe.Pointer(&err1)), unsafe.Sizeof(err1))
|
||||
for {
|
||||
rawSyscall(abi.FuncPCABI0(libc_exit_trampoline), 253, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
// Nothing to do.
|
||||
}
|
||||
745
src/syscall/exec_linux.go
Normal file
745
src/syscall/exec_linux.go
Normal file
@@ -0,0 +1,745 @@
|
||||
// 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 (
|
||||
"internal/itoa"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 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).
|
||||
//
|
||||
// Note that User Namespaces are not available on a number of popular Linux
|
||||
// versions (due to security issues), or are available but subject to AppArmor
|
||||
// restrictions like in Ubuntu 24.04.
|
||||
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.
|
||||
Unshareflags uintptr // Flags for unshare calls.
|
||||
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.
|
||||
UseCgroupFD bool // Whether to make use of the CgroupFD field.
|
||||
CgroupFD int // File descriptor of a cgroup to put the new process into.
|
||||
// PidFD, if not nil, is used to store the pidfd of a child, if the
|
||||
// functionality is supported by the kernel, or -1. Note *PidFD is
|
||||
// changed only if the process starts successfully.
|
||||
PidFD *int
|
||||
}
|
||||
|
||||
var (
|
||||
none = [...]byte{'n', 'o', 'n', 'e', 0}
|
||||
slash = [...]byte{'/', 0}
|
||||
|
||||
forceClone3 = false // Used by unit tests only.
|
||||
)
|
||||
|
||||
// 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.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv, envv []*byte, chroot, dir *byte, attr *ProcAttr, sys *SysProcAttr, pipe int) (pid int, err Errno) {
|
||||
// Set up and fork. This returns immediately in the parent or
|
||||
// if there's an error.
|
||||
upid, pidfd, 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.PidFD != nil {
|
||||
*sys.PidFD = int(pidfd)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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, pidfd int32, err1 Errno, mapPipe [2]int, locked bool) {
|
||||
// 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
|
||||
)
|
||||
pidfd = -1
|
||||
|
||||
rlim := origRlimitNofile.Load()
|
||||
|
||||
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
|
||||
}
|
||||
if sys.PidFD != nil {
|
||||
flags |= CLONE_PIDFD
|
||||
}
|
||||
// Whether to use clone3.
|
||||
if sys.UseCgroupFD || flags&CLONE_NEWTIME != 0 || forceClone3 {
|
||||
clone3 = &cloneArgs{
|
||||
flags: uint64(flags),
|
||||
exitSignal: uint64(SIGCHLD),
|
||||
}
|
||||
if sys.UseCgroupFD {
|
||||
clone3.flags |= CLONE_INTO_CGROUP
|
||||
clone3.cgroup = uint64(sys.CgroupFD)
|
||||
}
|
||||
if sys.PidFD != nil {
|
||||
clone3.pidFD = uint64(uintptr(unsafe.Pointer(&pidfd)))
|
||||
}
|
||||
}
|
||||
|
||||
// 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), 0)
|
||||
} 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, uintptr(unsafe.Pointer(&pidfd)))
|
||||
} else {
|
||||
pid, err1 = rawVforkSyscall(SYS_CLONE, flags, 0, uintptr(unsafe.Pointer(&pidfd)))
|
||||
}
|
||||
}
|
||||
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, fd1, uintptr(unsafe.Pointer(&setgroups[0])), uintptr(len(setgroups)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
if _, _, err1 = RawSyscall(SYS_CLOSE, 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, fd1, uintptr(unsafe.Pointer(&gidmap[0])), uintptr(len(gidmap)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
if _, _, err1 = RawSyscall(SYS_CLOSE, 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, fd1, uintptr(unsafe.Pointer(&uidmap[0])), uintptr(len(uidmap)))
|
||||
if err1 != 0 {
|
||||
goto childerror
|
||||
}
|
||||
if _, _, err1 = RawSyscall(SYS_CLOSE, 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 rlim != nil {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
func formatIDMappings(idMap []SysProcIDMap) []byte {
|
||||
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
|
||||
}
|
||||
|
||||
// writeIDMappings writes the user namespace User ID or Group ID mappings to the specified path.
|
||||
func writeIDMappings(path string, idMap []SysProcIDMap) error {
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
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
|
||||
}
|
||||
|
||||
// forkAndExecFailureCleanup cleans up after an exec failure.
|
||||
func forkAndExecFailureCleanup(attr *ProcAttr, sys *SysProcAttr) {
|
||||
if sys.PidFD != nil && *sys.PidFD != -1 {
|
||||
Close(*sys.PidFD)
|
||||
*sys.PidFD = -1
|
||||
}
|
||||
}
|
||||
758
src/syscall/exec_linux_test.go
Normal file
758
src/syscall/exec_linux_test.go
Normal file
@@ -0,0 +1,758 @@
|
||||
// Copyright 2015 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_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"internal/platform"
|
||||
"internal/syscall/unix"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// whoamiNEWUSER returns a command that runs "whoami" with CLONE_NEWUSER,
|
||||
// mapping uid and gid 0 to the actual uid and gid of the test.
|
||||
func whoamiNEWUSER(t *testing.T, uid, gid int, setgroups bool) *exec.Cmd {
|
||||
t.Helper()
|
||||
testenv.MustHaveExecPath(t, "whoami")
|
||||
cmd := testenv.Command(t, "whoami")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Cloneflags: syscall.CLONE_NEWUSER,
|
||||
UidMappings: []syscall.SysProcIDMap{
|
||||
{ContainerID: 0, HostID: uid, Size: 1},
|
||||
},
|
||||
GidMappings: []syscall.SysProcIDMap{
|
||||
{ContainerID: 0, HostID: gid, Size: 1},
|
||||
},
|
||||
GidMappingsEnableSetgroups: setgroups,
|
||||
}
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestCloneNEWUSERAndRemap(t *testing.T) {
|
||||
for _, setgroups := range []bool{false, true} {
|
||||
setgroups := setgroups
|
||||
t.Run(fmt.Sprintf("setgroups=%v", setgroups), func(t *testing.T) {
|
||||
uid := os.Getuid()
|
||||
gid := os.Getgid()
|
||||
|
||||
cmd := whoamiNEWUSER(t, uid, gid, setgroups)
|
||||
out, err := cmd.CombinedOutput()
|
||||
t.Logf("%v: %v", cmd, err)
|
||||
|
||||
if uid != 0 && setgroups {
|
||||
t.Logf("as non-root, expected permission error due to unprivileged gid_map")
|
||||
if !os.IsPermission(err) {
|
||||
if err == nil {
|
||||
t.Skipf("unexpected success: probably old kernel without security fix?")
|
||||
}
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: CLONE_NEWUSER appears to be unsupported")
|
||||
}
|
||||
t.Fatalf("got non-permission error") // Already logged above.
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
// May be inside a container that disallows CLONE_NEWUSER.
|
||||
t.Skipf("skipping: CLONE_NEWUSER appears to be unsupported")
|
||||
}
|
||||
t.Fatalf("unexpected command failure; output:\n%s", out)
|
||||
}
|
||||
|
||||
sout := strings.TrimSpace(string(out))
|
||||
want := "root"
|
||||
if sout != want {
|
||||
t.Fatalf("whoami = %q; want %q", out, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyCredGroupsDisableSetgroups(t *testing.T) {
|
||||
cmd := whoamiNEWUSER(t, os.Getuid(), os.Getgid(), false)
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: %v: %v", cmd, err)
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnshare(t *testing.T) {
|
||||
path := "/proc/net/dev"
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
t.Skip("kernel doesn't support proc filesystem")
|
||||
}
|
||||
if os.IsPermission(err) {
|
||||
t.Skip("unable to test proc filesystem due to permissions")
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
b, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
orig := strings.TrimSpace(string(b))
|
||||
if strings.Contains(orig, "lo:") && strings.Count(orig, ":") == 1 {
|
||||
// This test expects there to be at least 1 more network interface
|
||||
// in addition to the local network interface, so that it can tell
|
||||
// that unshare worked.
|
||||
t.Skip("not enough network interfaces to test unshare with")
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, "cat", path)
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Unshareflags: syscall.CLONE_NEWNET,
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
// CLONE_NEWNET does not appear to be supported.
|
||||
t.Skipf("skipping due to permission error: %v", err)
|
||||
}
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
|
||||
// Check there is only the local network interface.
|
||||
sout := strings.TrimSpace(string(out))
|
||||
if !strings.Contains(sout, "lo:") {
|
||||
t.Fatalf("Expected lo network interface to exist, got %s", sout)
|
||||
}
|
||||
|
||||
origLines := strings.Split(orig, "\n")
|
||||
lines := strings.Split(sout, "\n")
|
||||
if len(lines) >= len(origLines) {
|
||||
t.Logf("%s before unshare:\n%s", path, orig)
|
||||
t.Logf("%s after unshare:\n%s", path, sout)
|
||||
t.Fatalf("Got %d lines of output, want < %d", len(lines), len(origLines))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupCleanup(t *testing.T) {
|
||||
testenv.MustHaveExecPath(t, "id")
|
||||
cmd := testenv.Command(t, "id")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: 0,
|
||||
Gid: 0,
|
||||
},
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: %v: %v", cmd, err)
|
||||
}
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
strOut := strings.TrimSpace(string(out))
|
||||
t.Logf("id: %s", strOut)
|
||||
|
||||
expected := "uid=0(root) gid=0(root)"
|
||||
// Just check prefix because some distros reportedly output a
|
||||
// context parameter; see https://golang.org/issue/16224.
|
||||
// Alpine does not output groups; see https://golang.org/issue/19938.
|
||||
if !strings.HasPrefix(strOut, expected) {
|
||||
t.Errorf("expected prefix: %q", expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupCleanupUserNamespace(t *testing.T) {
|
||||
testenv.MustHaveExecPath(t, "id")
|
||||
cmd := testenv.Command(t, "id")
|
||||
uid, gid := os.Getuid(), os.Getgid()
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Cloneflags: syscall.CLONE_NEWUSER,
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uint32(uid),
|
||||
Gid: uint32(gid),
|
||||
},
|
||||
UidMappings: []syscall.SysProcIDMap{
|
||||
{ContainerID: 0, HostID: uid, Size: 1},
|
||||
},
|
||||
GidMappings: []syscall.SysProcIDMap{
|
||||
{ContainerID: 0, HostID: gid, Size: 1},
|
||||
},
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: %v: %v", cmd, err)
|
||||
}
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
strOut := strings.TrimSpace(string(out))
|
||||
t.Logf("id: %s", strOut)
|
||||
|
||||
// As in TestGroupCleanup, just check prefix.
|
||||
// The actual groups and contexts seem to vary from one distro to the next.
|
||||
expected := "uid=0(root) gid=0(root) groups=0(root)"
|
||||
if !strings.HasPrefix(strOut, expected) {
|
||||
t.Errorf("expected prefix: %q", expected)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for https://go.dev/issue/19661: unshare fails because systemd
|
||||
// has forced / to be shared
|
||||
func TestUnshareMountNameSpace(t *testing.T) {
|
||||
const mountNotSupported = "mount is not supported: " // Output prefix indicating a test skip.
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
dir := flag.Args()[0]
|
||||
err := syscall.Mount("none", dir, "proc", 0, "")
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
fmt.Print(mountNotSupported, err)
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unshare: mount %s: %v\n", dir, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
testenv.MustHaveExec(t)
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
d := t.TempDir()
|
||||
t.Cleanup(func() {
|
||||
// If the subprocess fails to unshare the parent directory, force-unmount it
|
||||
// so that the test can clean it up.
|
||||
if _, err := os.Stat(d); err == nil {
|
||||
syscall.Unmount(d, syscall.MNT_FORCE)
|
||||
}
|
||||
})
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestUnshareMountNameSpace$", d)
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Unshareflags: syscall.CLONE_NEWNS}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: could not start process with CLONE_NEWNS: %v", err)
|
||||
}
|
||||
t.Fatalf("unshare failed: %v\n%s", err, out)
|
||||
} else if len(out) != 0 {
|
||||
if bytes.HasPrefix(out, []byte(mountNotSupported)) {
|
||||
t.Skipf("skipping: helper process reported %s", out)
|
||||
}
|
||||
t.Fatalf("unexpected output from helper process: %s", out)
|
||||
}
|
||||
|
||||
// How do we tell if the namespace was really unshared? It turns out
|
||||
// to be simple: just try to remove the directory. If it's still mounted
|
||||
// on the rm will fail with EBUSY.
|
||||
if err := os.Remove(d); err != nil {
|
||||
t.Errorf("rmdir failed on %v: %v", d, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for Issue 20103: unshare fails when chroot is used
|
||||
func TestUnshareMountNameSpaceChroot(t *testing.T) {
|
||||
const mountNotSupported = "mount is not supported: " // Output prefix indicating a test skip.
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
dir := flag.Args()[0]
|
||||
err := syscall.Mount("none", dir, "proc", 0, "")
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
fmt.Print(mountNotSupported, err)
|
||||
} else if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "unshare: mount %s: %v\n", dir, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
d := t.TempDir()
|
||||
|
||||
// Since we are doing a chroot, we need the binary there,
|
||||
// and it must be statically linked.
|
||||
testenv.MustHaveGoBuild(t)
|
||||
if platform.MustLinkExternal(runtime.GOOS, runtime.GOARCH, false) {
|
||||
t.Skipf("skipping: can't build static binary because %s/%s requires external linking", runtime.GOOS, runtime.GOARCH)
|
||||
}
|
||||
x := filepath.Join(d, "syscall.test")
|
||||
t.Cleanup(func() {
|
||||
// If the subprocess fails to unshare the parent directory, force-unmount it
|
||||
// so that the test can clean it up.
|
||||
if _, err := os.Stat(d); err == nil {
|
||||
syscall.Unmount(d, syscall.MNT_FORCE)
|
||||
}
|
||||
})
|
||||
|
||||
cmd := testenv.Command(t, testenv.GoToolPath(t), "test", "-c", "-o", x, "syscall")
|
||||
cmd.Env = append(cmd.Environ(), "CGO_ENABLED=0")
|
||||
if o, err := cmd.CombinedOutput(); err != nil {
|
||||
t.Fatalf("%v: %v\n%s", cmd, err, o)
|
||||
}
|
||||
|
||||
cmd = testenv.Command(t, "/syscall.test", "-test.run=^TestUnshareMountNameSpaceChroot$", "/")
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Chroot: d, Unshareflags: syscall.CLONE_NEWNS}
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: could not start process with CLONE_NEWNS and Chroot %q: %v", d, err)
|
||||
}
|
||||
t.Fatalf("unshare failed: %v\n%s", err, out)
|
||||
} else if len(out) != 0 {
|
||||
if bytes.HasPrefix(out, []byte(mountNotSupported)) {
|
||||
t.Skipf("skipping: helper process reported %s", out)
|
||||
}
|
||||
t.Fatalf("unexpected output from helper process: %s", out)
|
||||
}
|
||||
|
||||
// How do we tell if the namespace was really unshared? It turns out
|
||||
// to be simple: just try to remove the executable. If it's still mounted
|
||||
// on, the rm will fail.
|
||||
if err := os.Remove(x); err != nil {
|
||||
t.Errorf("rm failed on %v: %v", x, err)
|
||||
}
|
||||
if err := os.Remove(d); err != nil {
|
||||
t.Errorf("rmdir failed on %v: %v", d, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test for Issue 29789: unshare fails when uid/gid mapping is specified
|
||||
func TestUnshareUidGidMapping(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
defer os.Exit(0)
|
||||
if err := syscall.Chroot(os.TempDir()); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getuid() == 0 {
|
||||
t.Skip("test exercises unprivileged user namespace, fails with privileges")
|
||||
}
|
||||
|
||||
testenv.MustHaveExec(t)
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestUnshareUidGidMapping$")
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Unshareflags: syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
|
||||
GidMappingsEnableSetgroups: false,
|
||||
UidMappings: []syscall.SysProcIDMap{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: syscall.Getuid(),
|
||||
Size: 1,
|
||||
},
|
||||
},
|
||||
GidMappings: []syscall.SysProcIDMap{
|
||||
{
|
||||
ContainerID: 0,
|
||||
HostID: syscall.Getgid(),
|
||||
Size: 1,
|
||||
},
|
||||
},
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: could not start process with CLONE_NEWNS and CLONE_NEWUSER: %v", err)
|
||||
}
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
}
|
||||
|
||||
func prepareCgroupFD(t *testing.T) (int, string) {
|
||||
t.Helper()
|
||||
|
||||
const O_PATH = 0x200000 // Same for all architectures, but for some reason not defined in syscall for 386||amd64.
|
||||
|
||||
// Requires cgroup v2.
|
||||
const prefix = "/sys/fs/cgroup"
|
||||
selfCg, err := os.ReadFile("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || os.IsPermission(err) {
|
||||
t.Skip(err)
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Expect a single line like this:
|
||||
// 0::/user.slice/user-1000.slice/user@1000.service/app.slice/vte-spawn-891992a2-efbb-4f28-aedb-b24f9e706770.scope
|
||||
// Otherwise it's either cgroup v1 or a hybrid hierarchy.
|
||||
if bytes.Count(selfCg, []byte("\n")) > 1 {
|
||||
t.Skip("cgroup v2 not available")
|
||||
}
|
||||
cg := bytes.TrimPrefix(selfCg, []byte("0::"))
|
||||
if len(cg) == len(selfCg) { // No prefix found.
|
||||
t.Skipf("cgroup v2 not available (/proc/self/cgroup contents: %q)", selfCg)
|
||||
}
|
||||
|
||||
// Need an ability to create a sub-cgroup.
|
||||
subCgroup, err := os.MkdirTemp(prefix+string(bytes.TrimSpace(cg)), "subcg-")
|
||||
if err != nil {
|
||||
// ErrPermission or EROFS (#57262) when running in an unprivileged container.
|
||||
// ErrNotExist when cgroupfs is not mounted in chroot/schroot.
|
||||
if os.IsNotExist(err) || testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: %v", err)
|
||||
}
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() { syscall.Rmdir(subCgroup) })
|
||||
|
||||
cgroupFD, err := syscall.Open(subCgroup, O_PATH, 0)
|
||||
if err != nil {
|
||||
t.Fatal(&os.PathError{Op: "open", Path: subCgroup, Err: err})
|
||||
}
|
||||
t.Cleanup(func() { syscall.Close(cgroupFD) })
|
||||
|
||||
return cgroupFD, "/" + path.Base(subCgroup)
|
||||
}
|
||||
|
||||
func TestUseCgroupFD(t *testing.T) {
|
||||
testenv.MustHaveExec(t)
|
||||
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
// Read and print own cgroup path.
|
||||
selfCg, err := os.ReadFile("/proc/self/cgroup")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Print(string(selfCg))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
fd, suffix := prepareCgroupFD(t)
|
||||
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestUseCgroupFD$")
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
UseCgroupFD: true,
|
||||
CgroupFD: fd,
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) && !errors.Is(err, syscall.EINVAL) {
|
||||
// Can be one of:
|
||||
// - clone3 not supported (old kernel);
|
||||
// - clone3 not allowed (by e.g. seccomp);
|
||||
// - lack of CAP_SYS_ADMIN.
|
||||
t.Skipf("clone3 with CLONE_INTO_CGROUP not available: %v", err)
|
||||
}
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
// NB: this wouldn't work with cgroupns.
|
||||
if !bytes.HasSuffix(bytes.TrimSpace(out), []byte(suffix)) {
|
||||
t.Fatalf("got: %q, want: a line that ends with %q", out, suffix)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCloneTimeNamespace(t *testing.T) {
|
||||
testenv.MustHaveExec(t)
|
||||
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
timens, err := os.Readlink("/proc/self/ns/time")
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
fmt.Print(string(timens))
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestCloneTimeNamespace$")
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Cloneflags: syscall.CLONE_NEWTIME,
|
||||
}
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
// CLONE_NEWTIME does not appear to be supported.
|
||||
t.Skipf("skipping, CLONE_NEWTIME not supported: %v", err)
|
||||
}
|
||||
t.Fatalf("Cmd failed with err %v, output: %s", err, out)
|
||||
}
|
||||
|
||||
// Inode number of the time namespaces should be different.
|
||||
// Based on https://man7.org/linux/man-pages/man7/time_namespaces.7.html#EXAMPLES
|
||||
timens, err := os.Readlink("/proc/self/ns/time")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
parentTimeNS := timens
|
||||
childTimeNS := string(out)
|
||||
if childTimeNS == parentTimeNS {
|
||||
t.Fatalf("expected child time namespace to be different from parent time namespace: %s", parentTimeNS)
|
||||
}
|
||||
}
|
||||
|
||||
func testPidFD(t *testing.T, userns bool) error {
|
||||
testenv.MustHaveExec(t)
|
||||
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
// Child: wait for a signal.
|
||||
time.Sleep(time.Hour)
|
||||
}
|
||||
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var pidfd int
|
||||
cmd := testenv.Command(t, exe, "-test.run=^TestPidFD$")
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
PidFD: &pidfd,
|
||||
}
|
||||
if userns {
|
||||
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
|
||||
}
|
||||
if err := cmd.Start(); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
cmd.Process.Kill()
|
||||
cmd.Wait()
|
||||
}()
|
||||
t.Log("got pidfd:", pidfd)
|
||||
// If pidfd is not supported by the kernel, -1 is returned.
|
||||
if pidfd == -1 {
|
||||
t.Skip("pidfd not supported")
|
||||
}
|
||||
defer syscall.Close(pidfd)
|
||||
|
||||
// Use pidfd to send a signal to the child.
|
||||
sig := syscall.SIGINT
|
||||
if err := unix.PidFDSendSignal(uintptr(pidfd), sig); err != nil {
|
||||
if err != syscall.EINVAL && testenv.SyscallIsNotSupported(err) {
|
||||
t.Skip("pidfd_send_signal syscall not supported:", err)
|
||||
}
|
||||
t.Fatal("pidfd_send_signal syscall failed:", err)
|
||||
}
|
||||
// Check if the child received our signal.
|
||||
err = cmd.Wait()
|
||||
if cmd.ProcessState == nil || cmd.ProcessState.Sys().(syscall.WaitStatus).Signal() != sig {
|
||||
t.Fatal("unexpected child error:", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestPidFD(t *testing.T) {
|
||||
if err := testPidFD(t, false); err != nil {
|
||||
t.Fatal("can't start a process:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPidFDWithUserNS(t *testing.T) {
|
||||
if err := testPidFD(t, true); err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skip("userns not supported:", err)
|
||||
}
|
||||
t.Fatal("can't start a process:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPidFDClone3(t *testing.T) {
|
||||
*syscall.ForceClone3 = true
|
||||
defer func() { *syscall.ForceClone3 = false }()
|
||||
|
||||
if err := testPidFD(t, false); err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skip("clone3 not supported:", err)
|
||||
}
|
||||
t.Fatal("can't start a process:", err)
|
||||
}
|
||||
}
|
||||
|
||||
type capHeader struct {
|
||||
version uint32
|
||||
pid int32
|
||||
}
|
||||
|
||||
type capData struct {
|
||||
effective uint32
|
||||
permitted uint32
|
||||
inheritable uint32
|
||||
}
|
||||
|
||||
const CAP_SYS_TIME = 25
|
||||
const CAP_SYSLOG = 34
|
||||
|
||||
type caps struct {
|
||||
hdr capHeader
|
||||
data [2]capData
|
||||
}
|
||||
|
||||
func getCaps() (caps, error) {
|
||||
var c caps
|
||||
|
||||
// Get capability version
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 {
|
||||
return c, fmt.Errorf("SYS_CAPGET: %v", errno)
|
||||
}
|
||||
|
||||
// Get current capabilities
|
||||
if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 {
|
||||
return c, fmt.Errorf("SYS_CAPGET: %v", errno)
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func TestAmbientCaps(t *testing.T) {
|
||||
testAmbientCaps(t, false)
|
||||
}
|
||||
|
||||
func TestAmbientCapsUserns(t *testing.T) {
|
||||
b, err := os.ReadFile("/proc/sys/kernel/apparmor_restrict_unprivileged_userns")
|
||||
if err == nil && strings.TrimSpace(string(b)) == "1" {
|
||||
t.Skip("AppArmor restriction for unprivileged user namespaces is enabled")
|
||||
}
|
||||
testAmbientCaps(t, true)
|
||||
}
|
||||
|
||||
func testAmbientCaps(t *testing.T, userns bool) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "1" {
|
||||
caps, err := getCaps()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(2)
|
||||
}
|
||||
if caps.data[0].effective&(1<<uint(CAP_SYS_TIME)) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "CAP_SYS_TIME unexpectedly not in the effective capability mask")
|
||||
os.Exit(2)
|
||||
}
|
||||
if caps.data[1].effective&(1<<uint(CAP_SYSLOG&31)) == 0 {
|
||||
fmt.Fprintln(os.Stderr, "CAP_SYSLOG unexpectedly not in the effective capability mask")
|
||||
os.Exit(2)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// skip on android, due to lack of lookup support
|
||||
if runtime.GOOS == "android" {
|
||||
t.Skip("skipping test on android; see Issue 27327")
|
||||
}
|
||||
|
||||
u, err := user.Lookup("nobody")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
uid, err := strconv.ParseInt(u.Uid, 0, 32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
gid, err := strconv.ParseInt(u.Gid, 0, 32)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Copy the test binary to a temporary location which is readable by nobody.
|
||||
f, err := os.CreateTemp("", "gotest")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
})
|
||||
|
||||
testenv.MustHaveExec(t)
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
e, err := os.Open(exe)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer e.Close()
|
||||
if _, err := io.Copy(f, e); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Chmod(0755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, f.Name(), "-test.run=^"+t.Name()+"$")
|
||||
cmd.Env = append(cmd.Environ(), "GO_WANT_HELPER_PROCESS=1")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||
Credential: &syscall.Credential{
|
||||
Uid: uint32(uid),
|
||||
Gid: uint32(gid),
|
||||
},
|
||||
AmbientCaps: []uintptr{CAP_SYS_TIME, CAP_SYSLOG},
|
||||
}
|
||||
if userns {
|
||||
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWUSER
|
||||
const nobody = 65534
|
||||
uid := os.Getuid()
|
||||
gid := os.Getgid()
|
||||
cmd.SysProcAttr.UidMappings = []syscall.SysProcIDMap{{
|
||||
ContainerID: int(nobody),
|
||||
HostID: uid,
|
||||
Size: int(1),
|
||||
}}
|
||||
cmd.SysProcAttr.GidMappings = []syscall.SysProcIDMap{{
|
||||
ContainerID: int(nobody),
|
||||
HostID: gid,
|
||||
Size: int(1),
|
||||
}}
|
||||
|
||||
// Set credentials to run as user and group nobody.
|
||||
cmd.SysProcAttr.Credential = &syscall.Credential{
|
||||
Uid: nobody,
|
||||
Gid: nobody,
|
||||
}
|
||||
}
|
||||
if err := cmd.Run(); err != nil {
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
t.Skipf("skipping: %v: %v", cmd, err)
|
||||
}
|
||||
t.Fatal(err.Error())
|
||||
}
|
||||
}
|
||||
187
src/syscall/exec_pdeathsig_test.go
Normal file
187
src/syscall/exec_pdeathsig_test.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2015 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 freebsd || linux
|
||||
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestDeathSignalSetuid verifies that a command run with a different UID still
|
||||
// receives PDeathsig; it is a regression test for https://go.dev/issue/9686.
|
||||
func TestDeathSignalSetuid(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skipf("skipping test that copies its binary into temp dir")
|
||||
}
|
||||
|
||||
// Copy the test binary to a location that another user can read/execute
|
||||
// after we drop privileges.
|
||||
//
|
||||
// TODO(bcmills): Why do we believe that another users will be able to
|
||||
// execute a binary in this directory? (It could be mounted noexec.)
|
||||
tempDir, err := os.MkdirTemp("", "TestDeathSignal")
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create temporary directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(tempDir)
|
||||
os.Chmod(tempDir, 0755)
|
||||
|
||||
tmpBinary := filepath.Join(tempDir, filepath.Base(os.Args[0]))
|
||||
|
||||
src, err := os.Open(os.Args[0])
|
||||
if err != nil {
|
||||
t.Fatalf("cannot open binary %q, %v", os.Args[0], err)
|
||||
}
|
||||
defer src.Close()
|
||||
|
||||
dst, err := os.OpenFile(tmpBinary, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("cannot create temporary binary %q, %v", tmpBinary, err)
|
||||
}
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
t.Fatalf("failed to copy test binary to %q, %v", tmpBinary, err)
|
||||
}
|
||||
err = dst.Close()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to close test binary %q, %v", tmpBinary, err)
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, tmpBinary)
|
||||
cmd.Env = append(cmd.Environ(), "GO_DEATHSIG_PARENT=1")
|
||||
chldStdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new stdin pipe: %v", err)
|
||||
}
|
||||
chldStdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create new stdout pipe: %v", err)
|
||||
}
|
||||
stderr := new(strings.Builder)
|
||||
cmd.Stderr = stderr
|
||||
|
||||
err = cmd.Start()
|
||||
defer func() {
|
||||
chldStdin.Close()
|
||||
cmd.Wait()
|
||||
if stderr.Len() > 0 {
|
||||
t.Logf("stderr:\n%s", stderr)
|
||||
}
|
||||
}()
|
||||
if err != nil {
|
||||
t.Fatalf("failed to start first child process: %v", err)
|
||||
}
|
||||
|
||||
chldPipe := bufio.NewReader(chldStdout)
|
||||
|
||||
if got, err := chldPipe.ReadString('\n'); got == "start\n" {
|
||||
syscall.Kill(cmd.Process.Pid, syscall.SIGTERM)
|
||||
|
||||
want := "ok\n"
|
||||
if got, err = chldPipe.ReadString('\n'); got != want {
|
||||
t.Fatalf("expected %q, received %q, %v", want, got, err)
|
||||
}
|
||||
} else if got == "skip\n" {
|
||||
t.Skipf("skipping: parent could not run child program as selected user")
|
||||
} else {
|
||||
t.Fatalf("did not receive start from child, received %q, %v", got, err)
|
||||
}
|
||||
}
|
||||
|
||||
func deathSignalParent() {
|
||||
var (
|
||||
u *user.User
|
||||
err error
|
||||
)
|
||||
if os.Getuid() == 0 {
|
||||
tryUsers := []string{"nobody"}
|
||||
if testenv.Builder() != "" {
|
||||
tryUsers = append(tryUsers, "gopher")
|
||||
}
|
||||
for _, name := range tryUsers {
|
||||
u, err = user.Lookup(name)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "Lookup(%q): %v\n", name, err)
|
||||
}
|
||||
}
|
||||
if u == nil {
|
||||
// If we couldn't find an unprivileged user to run as, try running as
|
||||
// the current user. (Empirically this still causes the call to Start to
|
||||
// fail with a permission error if running as a non-root user on Linux.)
|
||||
u, err = user.Current()
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
uid, err := strconv.ParseUint(u.Uid, 10, 32)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid UID: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
gid, err := strconv.ParseUint(u.Gid, 10, 32)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "invalid GID: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cmd := exec.Command(os.Args[0])
|
||||
cmd.Env = append(os.Environ(),
|
||||
"GO_DEATHSIG_PARENT=",
|
||||
"GO_DEATHSIG_CHILD=1",
|
||||
)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
attrs := syscall.SysProcAttr{
|
||||
Pdeathsig: syscall.SIGUSR1,
|
||||
Credential: &syscall.Credential{Uid: uint32(uid), Gid: uint32(gid)},
|
||||
}
|
||||
cmd.SysProcAttr = &attrs
|
||||
|
||||
fmt.Fprintf(os.Stderr, "starting process as user %q\n", u.Username)
|
||||
if err := cmd.Start(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
if testenv.SyscallIsNotSupported(err) {
|
||||
fmt.Println("skip")
|
||||
os.Exit(0)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
cmd.Wait()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func deathSignalChild() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGUSR1)
|
||||
go func() {
|
||||
<-c
|
||||
fmt.Println("ok")
|
||||
os.Exit(0)
|
||||
}()
|
||||
fmt.Println("start")
|
||||
|
||||
buf := make([]byte, 32)
|
||||
os.Stdin.Read(buf)
|
||||
|
||||
// We expected to be signaled before stdin closed
|
||||
fmt.Println("not ok")
|
||||
os.Exit(1)
|
||||
}
|
||||
611
src/syscall/exec_plan9.go
Normal file
611
src/syscall/exec_plan9.go
Normal file
@@ -0,0 +1,611 @@
|
||||
// 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.
|
||||
|
||||
// Fork, exec, wait, etc.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"internal/itoa"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ForkLock is not used on plan9.
|
||||
var ForkLock sync.RWMutex
|
||||
|
||||
// gstringb reads a non-empty string from b, prefixed with a 16-bit length in little-endian order.
|
||||
// It returns the string as a byte slice, or nil if b is too short to contain the length or
|
||||
// the full string.
|
||||
//
|
||||
//go:nosplit
|
||||
func gstringb(b []byte) []byte {
|
||||
if len(b) < 2 {
|
||||
return nil
|
||||
}
|
||||
n, b := gbit16(b)
|
||||
if int(n) > len(b) {
|
||||
return nil
|
||||
}
|
||||
return b[:n]
|
||||
}
|
||||
|
||||
// Offset of the name field in a 9P directory entry - see UnmarshalDir() in dir_plan9.go
|
||||
const nameOffset = 39
|
||||
|
||||
// gdirname returns the first filename from a buffer of directory entries,
|
||||
// and a slice containing the remaining directory entries.
|
||||
// If the buffer doesn't start with a valid directory entry, the returned name is nil.
|
||||
//
|
||||
//go:nosplit
|
||||
func gdirname(buf []byte) (name []byte, rest []byte) {
|
||||
if len(buf) < 2 {
|
||||
return
|
||||
}
|
||||
size, buf := gbit16(buf)
|
||||
if size < STATFIXLEN || int(size) > len(buf) {
|
||||
return
|
||||
}
|
||||
name = gstringb(buf[nameOffset:size])
|
||||
rest = buf[size:]
|
||||
return
|
||||
}
|
||||
|
||||
// StringSlicePtr converts a slice of strings to a slice of pointers
|
||||
// to NUL-terminated byte arrays. If any string contains a NUL byte
|
||||
// this function panics instead of returning an error.
|
||||
//
|
||||
// Deprecated: Use SlicePtrFromStrings instead.
|
||||
func StringSlicePtr(ss []string) []*byte {
|
||||
bb := make([]*byte, len(ss)+1)
|
||||
for i := 0; i < len(ss); i++ {
|
||||
bb[i] = StringBytePtr(ss[i])
|
||||
}
|
||||
bb[len(ss)] = nil
|
||||
return bb
|
||||
}
|
||||
|
||||
// SlicePtrFromStrings converts a slice of strings to a slice of
|
||||
// pointers to NUL-terminated byte arrays. If any string contains
|
||||
// a NUL byte, it returns (nil, [EINVAL]).
|
||||
func SlicePtrFromStrings(ss []string) ([]*byte, error) {
|
||||
var err error
|
||||
bb := make([]*byte, len(ss)+1)
|
||||
for i := 0; i < len(ss); i++ {
|
||||
bb[i], err = BytePtrFromString(ss[i])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
bb[len(ss)] = nil
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
// readdirnames returns the names of files inside the directory represented by dirfd.
|
||||
func readdirnames(dirfd int) (names []string, err error) {
|
||||
names = make([]string, 0, 100)
|
||||
var buf [STATMAX]byte
|
||||
|
||||
for {
|
||||
n, e := Read(dirfd, buf[:])
|
||||
if e != nil {
|
||||
return nil, e
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
for b := buf[:n]; len(b) > 0; {
|
||||
var s []byte
|
||||
s, b = gdirname(b)
|
||||
if s == nil {
|
||||
return nil, ErrBadStat
|
||||
}
|
||||
names = append(names, string(s))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// name of the directory containing names and control files for all open file descriptors
|
||||
var dupdev, _ = BytePtrFromString("#d")
|
||||
|
||||
// forkAndExecInChild forks the process, calling dup onto 0..len(fd)
|
||||
// and finally invoking exec(argv0, argvv, envv) in the child.
|
||||
// If a dup or exec fails, it writes the error string to pipe.
|
||||
// (The pipe write end 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.
|
||||
// The calls to RawSyscall are okay because they are assembly
|
||||
// functions that do not grow the stack.
|
||||
//
|
||||
//go:norace
|
||||
func forkAndExecInChild(argv0 *byte, argv []*byte, envv []envItem, dir *byte, attr *ProcAttr, pipe int, rflag int) (pid int, err error) {
|
||||
// Declare all variables at top in case any
|
||||
// declarations require heap allocation (e.g., errbuf).
|
||||
var (
|
||||
r1 uintptr
|
||||
nextfd int
|
||||
i int
|
||||
clearenv int
|
||||
envfd int
|
||||
errbuf [ERRMAX]byte
|
||||
statbuf [STATMAX]byte
|
||||
dupdevfd int
|
||||
n int
|
||||
b []byte
|
||||
)
|
||||
|
||||
// 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++
|
||||
|
||||
if envv != nil {
|
||||
clearenv = RFCENVG
|
||||
}
|
||||
|
||||
// About to call fork.
|
||||
// No more allocation or calls of non-assembly functions.
|
||||
r1, _, _ = RawSyscall(SYS_RFORK, uintptr(RFPROC|RFFDG|RFREND|clearenv|rflag), 0, 0)
|
||||
|
||||
if r1 != 0 {
|
||||
if int32(r1) == -1 {
|
||||
return 0, NewError(errstr())
|
||||
}
|
||||
// parent; return PID
|
||||
return int(r1), nil
|
||||
}
|
||||
|
||||
// Fork succeeded, now in child.
|
||||
|
||||
// Close fds we don't need.
|
||||
r1, _, _ = RawSyscall(SYS_OPEN, uintptr(unsafe.Pointer(dupdev)), uintptr(O_RDONLY), 0)
|
||||
dupdevfd = int(r1)
|
||||
if dupdevfd == -1 {
|
||||
goto childerror
|
||||
}
|
||||
dirloop:
|
||||
for {
|
||||
r1, _, _ = RawSyscall6(SYS_PREAD, uintptr(dupdevfd), uintptr(unsafe.Pointer(&statbuf[0])), uintptr(len(statbuf)), ^uintptr(0), ^uintptr(0), 0)
|
||||
n = int(r1)
|
||||
switch n {
|
||||
case -1:
|
||||
goto childerror
|
||||
case 0:
|
||||
break dirloop
|
||||
}
|
||||
for b = statbuf[:n]; len(b) > 0; {
|
||||
var s []byte
|
||||
s, b = gdirname(b)
|
||||
if s == nil {
|
||||
copy(errbuf[:], ErrBadStat.Error())
|
||||
goto childerror1
|
||||
}
|
||||
if s[len(s)-1] == 'l' {
|
||||
// control file for descriptor <N> is named <N>ctl
|
||||
continue
|
||||
}
|
||||
closeFdExcept(int(atoi(s)), pipe, dupdevfd, fd)
|
||||
}
|
||||
}
|
||||
RawSyscall(SYS_CLOSE, uintptr(dupdevfd), 0, 0)
|
||||
|
||||
// Write new environment variables.
|
||||
if envv != nil {
|
||||
for i = 0; i < len(envv); i++ {
|
||||
r1, _, _ = RawSyscall(SYS_CREATE, uintptr(unsafe.Pointer(envv[i].name)), uintptr(O_WRONLY), uintptr(0666))
|
||||
|
||||
if int32(r1) == -1 {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
envfd = int(r1)
|
||||
|
||||
r1, _, _ = RawSyscall6(SYS_PWRITE, uintptr(envfd), uintptr(unsafe.Pointer(envv[i].value)), uintptr(envv[i].nvalue),
|
||||
^uintptr(0), ^uintptr(0), 0)
|
||||
|
||||
if int32(r1) == -1 || int(r1) != envv[i].nvalue {
|
||||
goto childerror
|
||||
}
|
||||
|
||||
r1, _, _ = RawSyscall(SYS_CLOSE, uintptr(envfd), 0, 0)
|
||||
|
||||
if int32(r1) == -1 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Chdir
|
||||
if dir != nil {
|
||||
r1, _, _ = RawSyscall(SYS_CHDIR, uintptr(unsafe.Pointer(dir)), 0, 0)
|
||||
if int32(r1) == -1 {
|
||||
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 {
|
||||
r1, _, _ = RawSyscall(SYS_DUP, uintptr(pipe), uintptr(nextfd), 0)
|
||||
if int32(r1) == -1 {
|
||||
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++
|
||||
}
|
||||
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(nextfd), 0)
|
||||
if int32(r1) == -1 {
|
||||
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 {
|
||||
continue
|
||||
}
|
||||
r1, _, _ = RawSyscall(SYS_DUP, uintptr(fd[i]), uintptr(i), 0)
|
||||
if int32(r1) == -1 {
|
||||
goto childerror
|
||||
}
|
||||
}
|
||||
|
||||
// Pass 3: close fd[i] if it was moved in the previous pass.
|
||||
for i = 0; i < len(fd); i++ {
|
||||
if fd[i] >= len(fd) {
|
||||
RawSyscall(SYS_CLOSE, uintptr(fd[i]), 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// Time to exec.
|
||||
r1, _, _ = RawSyscall(SYS_EXEC,
|
||||
uintptr(unsafe.Pointer(argv0)),
|
||||
uintptr(unsafe.Pointer(&argv[0])), 0)
|
||||
|
||||
childerror:
|
||||
// send error string on pipe
|
||||
RawSyscall(SYS_ERRSTR, uintptr(unsafe.Pointer(&errbuf[0])), uintptr(len(errbuf)), 0)
|
||||
childerror1:
|
||||
errbuf[len(errbuf)-1] = 0
|
||||
i = 0
|
||||
for i < len(errbuf) && errbuf[i] != 0 {
|
||||
i++
|
||||
}
|
||||
|
||||
RawSyscall6(SYS_PWRITE, uintptr(pipe), uintptr(unsafe.Pointer(&errbuf[0])), uintptr(i),
|
||||
^uintptr(0), ^uintptr(0), 0)
|
||||
|
||||
for {
|
||||
RawSyscall(SYS_EXITS, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
// close the numbered file descriptor, unless it is fd1, fd2, or a member of fds.
|
||||
//
|
||||
//go:nosplit
|
||||
func closeFdExcept(n int, fd1 int, fd2 int, fds []int) {
|
||||
if n == fd1 || n == fd2 {
|
||||
return
|
||||
}
|
||||
for _, fd := range fds {
|
||||
if n == fd {
|
||||
return
|
||||
}
|
||||
}
|
||||
RawSyscall(SYS_CLOSE, uintptr(n), 0, 0)
|
||||
}
|
||||
|
||||
func cexecPipe(p []int) error {
|
||||
e := Pipe(p)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
fd, e := Open("#d/"+itoa.Itoa(p[1]), O_RDWR|O_CLOEXEC)
|
||||
if e != nil {
|
||||
Close(p[0])
|
||||
Close(p[1])
|
||||
return e
|
||||
}
|
||||
|
||||
Close(p[1])
|
||||
p[1] = fd
|
||||
return nil
|
||||
}
|
||||
|
||||
type envItem struct {
|
||||
name *byte
|
||||
value *byte
|
||||
nvalue int
|
||||
}
|
||||
|
||||
type ProcAttr struct {
|
||||
Dir string // Current working directory.
|
||||
Env []string // Environment.
|
||||
Files []uintptr // File descriptors.
|
||||
Sys *SysProcAttr
|
||||
}
|
||||
|
||||
type SysProcAttr struct {
|
||||
Rfork int // additional flags to pass to rfork
|
||||
}
|
||||
|
||||
var zeroProcAttr ProcAttr
|
||||
var zeroSysProcAttr SysProcAttr
|
||||
|
||||
func forkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
|
||||
var (
|
||||
p [2]int
|
||||
n int
|
||||
errbuf [ERRMAX]byte
|
||||
wmsg Waitmsg
|
||||
)
|
||||
|
||||
if attr == nil {
|
||||
attr = &zeroProcAttr
|
||||
}
|
||||
sys := attr.Sys
|
||||
if sys == nil {
|
||||
sys = &zeroSysProcAttr
|
||||
}
|
||||
|
||||
p[0] = -1
|
||||
p[1] = -1
|
||||
|
||||
// Convert args to C form.
|
||||
argv0p, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
destDir := attr.Dir
|
||||
if destDir == "" {
|
||||
wdmu.Lock()
|
||||
destDir = wdStr
|
||||
wdmu.Unlock()
|
||||
}
|
||||
var dir *byte
|
||||
if destDir != "" {
|
||||
dir, err = BytePtrFromString(destDir)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
var envvParsed []envItem
|
||||
if attr.Env != nil {
|
||||
envvParsed = make([]envItem, 0, len(attr.Env))
|
||||
for _, v := range attr.Env {
|
||||
i := 0
|
||||
for i < len(v) && v[i] != '=' {
|
||||
i++
|
||||
}
|
||||
|
||||
envname, err := BytePtrFromString("/env/" + v[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
envvalue := make([]byte, len(v)-i)
|
||||
copy(envvalue, v[i+1:])
|
||||
envvParsed = append(envvParsed, envItem{envname, &envvalue[0], len(v) - i})
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate child status pipe close on exec.
|
||||
e := cexecPipe(p[:])
|
||||
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
|
||||
// Kick off child.
|
||||
pid, err = forkAndExecInChild(argv0p, argvp, envvParsed, dir, attr, p[1], sys.Rfork)
|
||||
|
||||
if err != nil {
|
||||
if p[0] >= 0 {
|
||||
Close(p[0])
|
||||
Close(p[1])
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Read child error status from pipe.
|
||||
Close(p[1])
|
||||
n, err = Read(p[0], errbuf[:])
|
||||
Close(p[0])
|
||||
|
||||
if err != nil || n != 0 {
|
||||
if n > 0 {
|
||||
err = NewError(string(errbuf[:n]))
|
||||
} else if err == nil {
|
||||
err = NewError("failed to read exec status")
|
||||
}
|
||||
|
||||
// Child failed; wait for it to exit, to make sure
|
||||
// the zombies don't accumulate.
|
||||
for wmsg.Pid != pid {
|
||||
Await(&wmsg)
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// Read got EOF, so pipe closed on exec, so exec succeeded.
|
||||
return pid, nil
|
||||
}
|
||||
|
||||
type waitErr struct {
|
||||
Waitmsg
|
||||
err error
|
||||
}
|
||||
|
||||
var procs struct {
|
||||
sync.Mutex
|
||||
waits map[int]chan *waitErr
|
||||
}
|
||||
|
||||
// startProcess starts a new goroutine, tied to the OS
|
||||
// thread, which runs the process and subsequently waits
|
||||
// for it to finish, communicating the process stats back
|
||||
// to any goroutines that may have been waiting on it.
|
||||
//
|
||||
// Such a dedicated goroutine is needed because on
|
||||
// Plan 9, only the parent thread can wait for a child,
|
||||
// whereas goroutines tend to jump OS threads (e.g.,
|
||||
// between starting a process and running Wait(), the
|
||||
// goroutine may have been rescheduled).
|
||||
func startProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
|
||||
type forkRet struct {
|
||||
pid int
|
||||
err error
|
||||
}
|
||||
|
||||
forkc := make(chan forkRet, 1)
|
||||
go func() {
|
||||
runtime.LockOSThread()
|
||||
var ret forkRet
|
||||
|
||||
ret.pid, ret.err = forkExec(argv0, argv, attr)
|
||||
// If fork fails there is nothing to wait for.
|
||||
if ret.err != nil || ret.pid == 0 {
|
||||
forkc <- ret
|
||||
return
|
||||
}
|
||||
|
||||
waitc := make(chan *waitErr, 1)
|
||||
|
||||
// Mark that the process is running.
|
||||
procs.Lock()
|
||||
if procs.waits == nil {
|
||||
procs.waits = make(map[int]chan *waitErr)
|
||||
}
|
||||
procs.waits[ret.pid] = waitc
|
||||
procs.Unlock()
|
||||
|
||||
forkc <- ret
|
||||
|
||||
var w waitErr
|
||||
for w.err == nil && w.Pid != ret.pid {
|
||||
w.err = Await(&w.Waitmsg)
|
||||
}
|
||||
waitc <- &w
|
||||
close(waitc)
|
||||
}()
|
||||
ret := <-forkc
|
||||
return ret.pid, ret.err
|
||||
}
|
||||
|
||||
// Combination of fork and exec, careful to be thread safe.
|
||||
func ForkExec(argv0 string, argv []string, attr *ProcAttr) (pid int, err error) {
|
||||
return startProcess(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 = startProcess(argv0, argv, attr)
|
||||
return pid, 0, err
|
||||
}
|
||||
|
||||
// Ordinary exec.
|
||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
||||
if envv != nil {
|
||||
r1, _, _ := RawSyscall(SYS_RFORK, RFCENVG, 0, 0)
|
||||
if int32(r1) == -1 {
|
||||
return NewError(errstr())
|
||||
}
|
||||
|
||||
for _, v := range envv {
|
||||
i := 0
|
||||
for i < len(v) && v[i] != '=' {
|
||||
i++
|
||||
}
|
||||
|
||||
fd, e := Create("/env/"+v[:i], O_WRONLY, 0666)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
_, e = Write(fd, []byte(v[i+1:]))
|
||||
if e != nil {
|
||||
Close(fd)
|
||||
return e
|
||||
}
|
||||
Close(fd)
|
||||
}
|
||||
}
|
||||
|
||||
argv0p, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, _, e1 := Syscall(SYS_EXEC,
|
||||
uintptr(unsafe.Pointer(argv0p)),
|
||||
uintptr(unsafe.Pointer(&argvp[0])),
|
||||
0)
|
||||
|
||||
return e1
|
||||
}
|
||||
|
||||
// WaitProcess waits until the pid of a
|
||||
// running process is found in the queue of
|
||||
// wait messages. It is used in conjunction
|
||||
// with [ForkExec]/[StartProcess] to wait for a
|
||||
// running process to exit.
|
||||
func WaitProcess(pid int, w *Waitmsg) (err error) {
|
||||
procs.Lock()
|
||||
ch := procs.waits[pid]
|
||||
procs.Unlock()
|
||||
|
||||
var wmsg *waitErr
|
||||
if ch != nil {
|
||||
wmsg = <-ch
|
||||
procs.Lock()
|
||||
if procs.waits[pid] == ch {
|
||||
delete(procs.waits, pid)
|
||||
}
|
||||
procs.Unlock()
|
||||
}
|
||||
if wmsg == nil {
|
||||
// ch was missing or ch is closed
|
||||
return NewError("process not found")
|
||||
}
|
||||
if wmsg.err != nil {
|
||||
return wmsg.err
|
||||
}
|
||||
if w != nil {
|
||||
*w = wmsg.Waitmsg
|
||||
}
|
||||
return nil
|
||||
}
|
||||
46
src/syscall/exec_solaris_test.go
Normal file
46
src/syscall/exec_solaris_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2015 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 solaris
|
||||
|
||||
package syscall
|
||||
|
||||
import "unsafe"
|
||||
|
||||
//go:cgo_import_dynamic libc_Getpgid getpgid "libc.so"
|
||||
//go:cgo_import_dynamic libc_Getpgrp getpgrp "libc.so"
|
||||
|
||||
//go:linkname libc_Getpgid libc_Getpgid
|
||||
//go:linkname libc_Getpgrp libc_Getpgrp
|
||||
|
||||
var (
|
||||
libc_Getpgid,
|
||||
libc_Getpgrp libcFunc
|
||||
)
|
||||
|
||||
func Getpgid(pid int) (pgid int, err error) {
|
||||
r0, _, e1 := sysvicall6(uintptr(unsafe.Pointer(&libc_Getpgid)), 1, uintptr(pid), 0, 0, 0, 0, 0)
|
||||
pgid = int(r0)
|
||||
if e1 != 0 {
|
||||
err = e1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func Getpgrp() (pgrp int) {
|
||||
r0, _, _ := sysvicall6(uintptr(unsafe.Pointer(&libc_Getpgrp)), 0, 0, 0, 0, 0, 0, 0)
|
||||
pgrp = int(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func Tcgetpgrp(fd int) (pgid int32, err error) {
|
||||
if errno := ioctlPtr(uintptr(fd), TIOCGPGRP, unsafe.Pointer(&pgid)); errno != 0 {
|
||||
return -1, errno
|
||||
}
|
||||
return pgid, nil
|
||||
}
|
||||
|
||||
func Tcsetpgrp(fd int, pgid int32) (err error) {
|
||||
return ioctlPtr(uintptr(fd), TIOCSPGRP, unsafe.Pointer(&pgid))
|
||||
}
|
||||
314
src/syscall/exec_unix.go
Normal file
314
src/syscall/exec_unix.go
Normal file
@@ -0,0 +1,314 @@
|
||||
// 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 (
|
||||
errorspkg "errors"
|
||||
"internal/bytealg"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// StringSlicePtr converts a slice of strings to a slice of pointers
|
||||
// to NUL-terminated byte arrays. If any string contains a NUL byte
|
||||
// this function panics instead of returning an error.
|
||||
//
|
||||
// Deprecated: Use [SlicePtrFromStrings] instead.
|
||||
func StringSlicePtr(ss []string) []*byte {
|
||||
bb := make([]*byte, len(ss)+1)
|
||||
for i := 0; i < len(ss); i++ {
|
||||
bb[i] = StringBytePtr(ss[i])
|
||||
}
|
||||
bb[len(ss)] = nil
|
||||
return bb
|
||||
}
|
||||
|
||||
// SlicePtrFromStrings converts a slice of strings to a slice of
|
||||
// pointers to NUL-terminated byte arrays. If any string contains
|
||||
// a NUL byte, it returns (nil, [EINVAL]).
|
||||
func SlicePtrFromStrings(ss []string) ([]*byte, error) {
|
||||
n := 0
|
||||
for _, s := range ss {
|
||||
if bytealg.IndexByteString(s, 0) != -1 {
|
||||
return nil, EINVAL
|
||||
}
|
||||
n += len(s) + 1 // +1 for NUL
|
||||
}
|
||||
bb := make([]*byte, len(ss)+1)
|
||||
b := make([]byte, n)
|
||||
n = 0
|
||||
for i, s := range ss {
|
||||
bb[i] = &b[n]
|
||||
copy(b[n:], s)
|
||||
n += len(s) + 1
|
||||
}
|
||||
return bb, nil
|
||||
}
|
||||
|
||||
func CloseOnExec(fd int) { fcntl(fd, F_SETFD, FD_CLOEXEC) }
|
||||
|
||||
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||
flag, err := fcntl(fd, F_GETFL, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (flag&O_NONBLOCK != 0) == nonblocking {
|
||||
return nil
|
||||
}
|
||||
if nonblocking {
|
||||
flag |= O_NONBLOCK
|
||||
} else {
|
||||
flag &^= O_NONBLOCK
|
||||
}
|
||||
_, err = fcntl(fd, F_SETFL, flag)
|
||||
return err
|
||||
}
|
||||
|
||||
// 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, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
envvp, err := SlicePtrFromStrings(attr.Env)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if (runtime.GOOS == "freebsd" || runtime.GOOS == "dragonfly") && len(argv) > 0 && len(argv[0]) > len(argv0) {
|
||||
argvp[0] = argv0p
|
||||
}
|
||||
|
||||
var chroot *byte
|
||||
if sys.Chroot != "" {
|
||||
chroot, err = BytePtrFromString(sys.Chroot)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
var dir *byte
|
||||
if attr.Dir != "" {
|
||||
dir, err = BytePtrFromString(attr.Dir)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
// Both Setctty and Foreground use the Ctty field,
|
||||
// but they give it slightly different meanings.
|
||||
if sys.Setctty && sys.Foreground {
|
||||
return 0, errorspkg.New("both Setctty and Foreground set in SysProcAttr")
|
||||
}
|
||||
if sys.Setctty && sys.Ctty >= len(attr.Files) {
|
||||
return 0, errorspkg.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 != EINTR {
|
||||
break
|
||||
}
|
||||
}
|
||||
Close(p[0])
|
||||
if err != nil || n != 0 {
|
||||
if n == int(unsafe.Sizeof(err1)) {
|
||||
err = Errno(err1)
|
||||
}
|
||||
if err == nil {
|
||||
err = EPIPE
|
||||
}
|
||||
|
||||
// Child failed; wait for it to exit, to make sure
|
||||
// the zombies don't accumulate.
|
||||
_, err1 := Wait4(pid, &wstatus, 0, nil)
|
||||
for err1 == EINTR {
|
||||
_, err1 = Wait4(pid, &wstatus, 0, nil)
|
||||
}
|
||||
|
||||
// OS-specific cleanup on failure.
|
||||
forkAndExecFailureCleanup(attr, sys)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// 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) {
|
||||
argv0p, err := BytePtrFromString(argv0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
argvp, err := SlicePtrFromStrings(argv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
envvp, err := SlicePtrFromStrings(envv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
runtime_BeforeExec()
|
||||
|
||||
rlim := origRlimitNofile.Load()
|
||||
if rlim != nil {
|
||||
Setrlimit(RLIMIT_NOFILE, rlim)
|
||||
}
|
||||
|
||||
var err1 error
|
||||
if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "aix" {
|
||||
// RawSyscall should never be used on Solaris, illumos, or AIX.
|
||||
err1 = execveLibc(
|
||||
uintptr(unsafe.Pointer(argv0p)),
|
||||
uintptr(unsafe.Pointer(&argvp[0])),
|
||||
uintptr(unsafe.Pointer(&envvp[0])))
|
||||
} else if runtime.GOOS == "darwin" || runtime.GOOS == "ios" {
|
||||
// Similarly on Darwin.
|
||||
err1 = execveDarwin(argv0p, &argvp[0], &envvp[0])
|
||||
} else if runtime.GOOS == "openbsd" && runtime.GOARCH != "mips64" {
|
||||
// Similarly on OpenBSD.
|
||||
err1 = execveOpenBSD(argv0p, &argvp[0], &envvp[0])
|
||||
} else {
|
||||
_, _, err1 = RawSyscall(SYS_EXECVE,
|
||||
uintptr(unsafe.Pointer(argv0p)),
|
||||
uintptr(unsafe.Pointer(&argvp[0])),
|
||||
uintptr(unsafe.Pointer(&envvp[0])))
|
||||
}
|
||||
runtime_AfterExec()
|
||||
return err1
|
||||
}
|
||||
394
src/syscall/exec_unix_test.go
Normal file
394
src/syscall/exec_unix_test.go
Normal file
@@ -0,0 +1,394 @@
|
||||
// Copyright 2015 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_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"internal/testenv"
|
||||
"io"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type command struct {
|
||||
pipe io.WriteCloser
|
||||
proc *exec.Cmd
|
||||
test *testing.T
|
||||
}
|
||||
|
||||
func (c *command) Info() (pid, pgrp int) {
|
||||
pid = c.proc.Process.Pid
|
||||
|
||||
pgrp, err := syscall.Getpgid(pid)
|
||||
if err != nil {
|
||||
c.test.Fatal(err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *command) Start() {
|
||||
if err := c.proc.Start(); err != nil {
|
||||
c.test.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *command) Stop() {
|
||||
c.pipe.Close()
|
||||
if err := c.proc.Wait(); err != nil {
|
||||
c.test.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func create(t *testing.T) *command {
|
||||
testenv.MustHaveExec(t)
|
||||
|
||||
proc := exec.Command("cat")
|
||||
stdin, err := proc.StdinPipe()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
return &command{stdin, proc, t}
|
||||
}
|
||||
|
||||
func parent() (pid, pgrp int) {
|
||||
return syscall.Getpid(), syscall.Getpgrp()
|
||||
}
|
||||
|
||||
func TestZeroSysProcAttr(t *testing.T) {
|
||||
ppid, ppgrp := parent()
|
||||
|
||||
cmd := create(t)
|
||||
|
||||
cmd.Start()
|
||||
defer cmd.Stop()
|
||||
|
||||
cpid, cpgrp := cmd.Info()
|
||||
|
||||
if cpid == ppid {
|
||||
t.Fatalf("Parent and child have the same process ID")
|
||||
}
|
||||
|
||||
if cpgrp != ppgrp {
|
||||
t.Fatalf("Child is not in parent's process group")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetpgid(t *testing.T) {
|
||||
ppid, ppgrp := parent()
|
||||
|
||||
cmd := create(t)
|
||||
|
||||
cmd.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd.Start()
|
||||
defer cmd.Stop()
|
||||
|
||||
cpid, cpgrp := cmd.Info()
|
||||
|
||||
if cpid == ppid {
|
||||
t.Fatalf("Parent and child have the same process ID")
|
||||
}
|
||||
|
||||
if cpgrp == ppgrp {
|
||||
t.Fatalf("Parent and child are in the same process group")
|
||||
}
|
||||
|
||||
if cpid != cpgrp {
|
||||
t.Fatalf("Child's process group is not the child's process ID")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPgid(t *testing.T) {
|
||||
ppid, ppgrp := parent()
|
||||
|
||||
cmd1 := create(t)
|
||||
|
||||
cmd1.proc.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd1.Start()
|
||||
defer cmd1.Stop()
|
||||
|
||||
cpid1, cpgrp1 := cmd1.Info()
|
||||
|
||||
if cpid1 == ppid {
|
||||
t.Fatalf("Parent and child 1 have the same process ID")
|
||||
}
|
||||
|
||||
if cpgrp1 == ppgrp {
|
||||
t.Fatalf("Parent and child 1 are in the same process group")
|
||||
}
|
||||
|
||||
if cpid1 != cpgrp1 {
|
||||
t.Fatalf("Child 1's process group is not its process ID")
|
||||
}
|
||||
|
||||
cmd2 := create(t)
|
||||
|
||||
cmd2.proc.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setpgid: true,
|
||||
Pgid: cpgrp1,
|
||||
}
|
||||
cmd2.Start()
|
||||
defer cmd2.Stop()
|
||||
|
||||
cpid2, cpgrp2 := cmd2.Info()
|
||||
|
||||
if cpid2 == ppid {
|
||||
t.Fatalf("Parent and child 2 have the same process ID")
|
||||
}
|
||||
|
||||
if cpgrp2 == ppgrp {
|
||||
t.Fatalf("Parent and child 2 are in the same process group")
|
||||
}
|
||||
|
||||
if cpid2 == cpgrp2 {
|
||||
t.Fatalf("Child 2's process group is its process ID")
|
||||
}
|
||||
|
||||
if cpid1 == cpid2 {
|
||||
t.Fatalf("Child 1 and 2 have the same process ID")
|
||||
}
|
||||
|
||||
if cpgrp1 != cpgrp2 {
|
||||
t.Fatalf("Child 1 and 2 are not in the same process group")
|
||||
}
|
||||
}
|
||||
|
||||
func TestForeground(t *testing.T) {
|
||||
signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
|
||||
defer signal.Reset()
|
||||
|
||||
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
t.Skipf("Can't test Foreground. Couldn't open /dev/tty: %s", err)
|
||||
}
|
||||
defer tty.Close()
|
||||
|
||||
ttyFD := int(tty.Fd())
|
||||
|
||||
fpgrp, err := syscall.Tcgetpgrp(ttyFD)
|
||||
if err != nil {
|
||||
t.Fatalf("Tcgetpgrp failed: %v", err)
|
||||
}
|
||||
if fpgrp == 0 {
|
||||
t.Fatalf("Foreground process group is zero")
|
||||
}
|
||||
|
||||
ppid, ppgrp := parent()
|
||||
|
||||
cmd := create(t)
|
||||
|
||||
cmd.proc.SysProcAttr = &syscall.SysProcAttr{
|
||||
Ctty: ttyFD,
|
||||
Foreground: true,
|
||||
}
|
||||
cmd.Start()
|
||||
|
||||
cpid, cpgrp := cmd.Info()
|
||||
|
||||
if cpid == ppid {
|
||||
t.Fatalf("Parent and child have the same process ID")
|
||||
}
|
||||
|
||||
if cpgrp == ppgrp {
|
||||
t.Fatalf("Parent and child are in the same process group")
|
||||
}
|
||||
|
||||
if cpid != cpgrp {
|
||||
t.Fatalf("Child's process group is not the child's process ID")
|
||||
}
|
||||
|
||||
cmd.Stop()
|
||||
|
||||
// This call fails on darwin/arm64. The failure doesn't matter, though.
|
||||
// This is just best effort.
|
||||
syscall.Tcsetpgrp(ttyFD, fpgrp)
|
||||
}
|
||||
|
||||
func TestForegroundSignal(t *testing.T) {
|
||||
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
t.Skipf("couldn't open /dev/tty: %s", err)
|
||||
}
|
||||
defer tty.Close()
|
||||
|
||||
ttyFD := int(tty.Fd())
|
||||
|
||||
fpgrp, err := syscall.Tcgetpgrp(ttyFD)
|
||||
if err != nil {
|
||||
t.Fatalf("Tcgetpgrp failed: %v", err)
|
||||
}
|
||||
if fpgrp == 0 {
|
||||
t.Fatalf("Foreground process group is zero")
|
||||
}
|
||||
|
||||
defer func() {
|
||||
signal.Ignore(syscall.SIGTTIN, syscall.SIGTTOU)
|
||||
syscall.Tcsetpgrp(ttyFD, fpgrp)
|
||||
signal.Reset()
|
||||
}()
|
||||
|
||||
ch1 := make(chan os.Signal, 1)
|
||||
ch2 := make(chan bool)
|
||||
|
||||
signal.Notify(ch1, syscall.SIGTTIN, syscall.SIGTTOU)
|
||||
defer signal.Stop(ch1)
|
||||
|
||||
cmd := create(t)
|
||||
|
||||
go func() {
|
||||
cmd.proc.SysProcAttr = &syscall.SysProcAttr{
|
||||
Ctty: ttyFD,
|
||||
Foreground: true,
|
||||
}
|
||||
cmd.Start()
|
||||
cmd.Stop()
|
||||
close(ch2)
|
||||
}()
|
||||
|
||||
timer := time.NewTimer(30 * time.Second)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case sig := <-ch1:
|
||||
t.Errorf("unexpected signal %v", sig)
|
||||
case <-ch2:
|
||||
// Success.
|
||||
return
|
||||
case <-timer.C:
|
||||
t.Fatal("timed out waiting for child process")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test a couple of cases that SysProcAttr can't handle. Issue 29458.
|
||||
func TestInvalidExec(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("SetCtty-Foreground", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cmd := create(t)
|
||||
cmd.proc.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setctty: true,
|
||||
Foreground: true,
|
||||
Ctty: 0,
|
||||
}
|
||||
if err := cmd.proc.Start(); err == nil {
|
||||
t.Error("expected error setting both SetCtty and Foreground")
|
||||
}
|
||||
})
|
||||
t.Run("invalid-Ctty", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
cmd := create(t)
|
||||
cmd.proc.SysProcAttr = &syscall.SysProcAttr{
|
||||
Setctty: true,
|
||||
Ctty: 3,
|
||||
}
|
||||
if err := cmd.proc.Start(); err == nil {
|
||||
t.Error("expected error with invalid Ctty value")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestExec is for issue #41702.
|
||||
func TestExec(t *testing.T) {
|
||||
testenv.MustHaveExec(t)
|
||||
cmd := exec.Command(os.Args[0], "-test.run=^TestExecHelper$")
|
||||
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=2")
|
||||
o, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("%s\n%v", o, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestExecHelper is used by TestExec. It does nothing by itself.
|
||||
// In testing on macOS 10.14, this used to fail with
|
||||
// "signal: illegal instruction" more than half the time.
|
||||
func TestExecHelper(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "2" {
|
||||
return
|
||||
}
|
||||
|
||||
// We don't have to worry about restoring these values.
|
||||
// We are in a child process that only runs this test,
|
||||
// and we are going to call syscall.Exec anyhow.
|
||||
os.Setenv("GO_WANT_HELPER_PROCESS", "3")
|
||||
|
||||
stop := time.Now().Add(time.Second)
|
||||
for i := 0; i < 100; i++ {
|
||||
go func(i int) {
|
||||
r := rand.New(rand.NewSource(int64(i)))
|
||||
for time.Now().Before(stop) {
|
||||
r.Uint64()
|
||||
}
|
||||
}(i)
|
||||
}
|
||||
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
|
||||
argv := []string{os.Args[0], "-test.run=^TestExecHelper$"}
|
||||
syscall.Exec(os.Args[0], argv, os.Environ())
|
||||
|
||||
t.Error("syscall.Exec returned")
|
||||
}
|
||||
|
||||
// Test that rlimit values are restored by exec.
|
||||
func TestRlimitRestored(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") != "" {
|
||||
fmt.Println(syscall.OrigRlimitNofile().Cur)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
orig := syscall.OrigRlimitNofile()
|
||||
if orig == nil {
|
||||
t.Skip("skipping test because rlimit not adjusted at startup")
|
||||
}
|
||||
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
executable = os.Args[0]
|
||||
}
|
||||
|
||||
cmd := testenv.Command(t, executable, "-test.run=^TestRlimitRestored$")
|
||||
cmd = testenv.CleanCmdEnv(cmd)
|
||||
cmd.Env = append(cmd.Env, "GO_WANT_HELPER_PROCESS=1")
|
||||
|
||||
out, err := cmd.CombinedOutput()
|
||||
if len(out) > 0 {
|
||||
t.Logf("%s", out)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("subprocess failed: %v", err)
|
||||
}
|
||||
s := string(bytes.TrimSpace(out))
|
||||
v, err := strconv.ParseUint(s, 10, 64)
|
||||
if err != nil {
|
||||
t.Fatalf("could not parse %q as number: %v", s, v)
|
||||
}
|
||||
|
||||
if v != uint64(orig.Cur) {
|
||||
t.Errorf("exec rlimit = %d, want %d", v, orig)
|
||||
}
|
||||
}
|
||||
|
||||
func TestForkExecNilArgv(t *testing.T) {
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
t.Fatal("forkExec panicked")
|
||||
}
|
||||
}()
|
||||
|
||||
// We don't really care what the result of forkExec is, just that it doesn't
|
||||
// panic, so we choose something we know won't actually spawn a process (probably).
|
||||
syscall.ForkExec("/dev/null", nil, nil)
|
||||
}
|
||||
404
src/syscall/exec_windows.go
Normal file
404
src/syscall/exec_windows.go
Normal file
@@ -0,0 +1,404 @@
|
||||
// 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.
|
||||
|
||||
// Fork, exec, wait, etc.
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"internal/bytealg"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ForkLock is not used on Windows.
|
||||
var ForkLock sync.RWMutex
|
||||
|
||||
// EscapeArg rewrites command line argument s as prescribed
|
||||
// in https://msdn.microsoft.com/en-us/library/ms880421.
|
||||
// This function returns "" (2 double quotes) if s is empty.
|
||||
// Alternatively, these transformations are done:
|
||||
// - every back slash (\) is doubled, but only if immediately
|
||||
// followed by double quote (");
|
||||
// - every double quote (") is escaped by back slash (\);
|
||||
// - finally, s is wrapped with double quotes (arg -> "arg"),
|
||||
// but only if there is space or tab inside s.
|
||||
func EscapeArg(s string) string {
|
||||
if len(s) == 0 {
|
||||
return `""`
|
||||
}
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"', '\\', ' ', '\t':
|
||||
// Some escaping required.
|
||||
b := make([]byte, 0, len(s)+2)
|
||||
b = appendEscapeArg(b, s)
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// appendEscapeArg escapes the string s, as per escapeArg,
|
||||
// appends the result to b, and returns the updated slice.
|
||||
func appendEscapeArg(b []byte, s string) []byte {
|
||||
if len(s) == 0 {
|
||||
return append(b, `""`...)
|
||||
}
|
||||
|
||||
needsBackslash := false
|
||||
hasSpace := false
|
||||
for i := 0; i < len(s); i++ {
|
||||
switch s[i] {
|
||||
case '"', '\\':
|
||||
needsBackslash = true
|
||||
case ' ', '\t':
|
||||
hasSpace = true
|
||||
}
|
||||
}
|
||||
|
||||
if !needsBackslash && !hasSpace {
|
||||
// No special handling required; normal case.
|
||||
return append(b, s...)
|
||||
}
|
||||
if !needsBackslash {
|
||||
// hasSpace is true, so we need to quote the string.
|
||||
b = append(b, '"')
|
||||
b = append(b, s...)
|
||||
return append(b, '"')
|
||||
}
|
||||
|
||||
if hasSpace {
|
||||
b = append(b, '"')
|
||||
}
|
||||
slashes := 0
|
||||
for i := 0; i < len(s); i++ {
|
||||
c := s[i]
|
||||
switch c {
|
||||
default:
|
||||
slashes = 0
|
||||
case '\\':
|
||||
slashes++
|
||||
case '"':
|
||||
for ; slashes > 0; slashes-- {
|
||||
b = append(b, '\\')
|
||||
}
|
||||
b = append(b, '\\')
|
||||
}
|
||||
b = append(b, c)
|
||||
}
|
||||
if hasSpace {
|
||||
for ; slashes > 0; slashes-- {
|
||||
b = append(b, '\\')
|
||||
}
|
||||
b = append(b, '"')
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// makeCmdLine builds a command line out of args by escaping "special"
|
||||
// characters and joining the arguments with spaces.
|
||||
func makeCmdLine(args []string) string {
|
||||
var b []byte
|
||||
for _, v := range args {
|
||||
if len(b) > 0 {
|
||||
b = append(b, ' ')
|
||||
}
|
||||
b = appendEscapeArg(b, v)
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// createEnvBlock converts an array of environment strings into
|
||||
// the representation required by CreateProcess: a sequence of NUL
|
||||
// terminated strings followed by a nil.
|
||||
// Last bytes are two UCS-2 NULs, or four NUL bytes.
|
||||
// If any string contains a NUL, it returns (nil, EINVAL).
|
||||
func createEnvBlock(envv []string) ([]uint16, error) {
|
||||
if len(envv) == 0 {
|
||||
return utf16.Encode([]rune("\x00\x00")), nil
|
||||
}
|
||||
var length int
|
||||
for _, s := range envv {
|
||||
if bytealg.IndexByteString(s, 0) != -1 {
|
||||
return nil, EINVAL
|
||||
}
|
||||
length += len(s) + 1
|
||||
}
|
||||
length += 1
|
||||
|
||||
b := make([]uint16, 0, length)
|
||||
for _, s := range envv {
|
||||
for _, c := range s {
|
||||
b = utf16.AppendRune(b, c)
|
||||
}
|
||||
b = utf16.AppendRune(b, 0)
|
||||
}
|
||||
b = utf16.AppendRune(b, 0)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func CloseOnExec(fd Handle) {
|
||||
SetHandleInformation(Handle(fd), HANDLE_FLAG_INHERIT, 0)
|
||||
}
|
||||
|
||||
func SetNonblock(fd Handle, nonblocking bool) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// FullPath retrieves the full path of the specified file.
|
||||
func FullPath(name string) (path string, err error) {
|
||||
p, err := UTF16PtrFromString(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
n := uint32(100)
|
||||
for {
|
||||
buf := make([]uint16, n)
|
||||
n, err = GetFullPathName(p, uint32(len(buf)), &buf[0], nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n <= uint32(len(buf)) {
|
||||
return UTF16ToString(buf[:n]), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isSlash(c uint8) bool {
|
||||
return c == '\\' || c == '/'
|
||||
}
|
||||
|
||||
func normalizeDir(dir string) (name string, err error) {
|
||||
ndir, err := FullPath(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(ndir) > 2 && isSlash(ndir[0]) && isSlash(ndir[1]) {
|
||||
// dir cannot have \\server\share\path form
|
||||
return "", EINVAL
|
||||
}
|
||||
return ndir, nil
|
||||
}
|
||||
|
||||
func volToUpper(ch int) int {
|
||||
if 'a' <= ch && ch <= 'z' {
|
||||
ch += 'A' - 'a'
|
||||
}
|
||||
return ch
|
||||
}
|
||||
|
||||
func joinExeDirAndFName(dir, p string) (name string, err error) {
|
||||
if len(p) == 0 {
|
||||
return "", EINVAL
|
||||
}
|
||||
if len(p) > 2 && isSlash(p[0]) && isSlash(p[1]) {
|
||||
// \\server\share\path form
|
||||
return p, nil
|
||||
}
|
||||
if len(p) > 1 && p[1] == ':' {
|
||||
// has drive letter
|
||||
if len(p) == 2 {
|
||||
return "", EINVAL
|
||||
}
|
||||
if isSlash(p[2]) {
|
||||
return p, nil
|
||||
} else {
|
||||
d, err := normalizeDir(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if volToUpper(int(p[0])) == volToUpper(int(d[0])) {
|
||||
return FullPath(d + "\\" + p[2:])
|
||||
} else {
|
||||
return FullPath(p)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// no drive letter
|
||||
d, err := normalizeDir(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if isSlash(p[0]) {
|
||||
return FullPath(d[:2] + p)
|
||||
} else {
|
||||
return FullPath(d + "\\" + p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type ProcAttr struct {
|
||||
Dir string
|
||||
Env []string
|
||||
Files []uintptr
|
||||
Sys *SysProcAttr
|
||||
}
|
||||
|
||||
type SysProcAttr struct {
|
||||
HideWindow bool
|
||||
CmdLine string // used if non-empty, else the windows command line is built by escaping the arguments passed to StartProcess
|
||||
CreationFlags uint32
|
||||
Token Token // if set, runs new process in the security context represented by the token
|
||||
ProcessAttributes *SecurityAttributes // if set, applies these security attributes as the descriptor for the new process
|
||||
ThreadAttributes *SecurityAttributes // if set, applies these security attributes as the descriptor for the main thread of the new process
|
||||
NoInheritHandles bool // if set, no handles are inherited by the new process, not even the standard handles, contained in ProcAttr.Files, nor the ones contained in AdditionalInheritedHandles
|
||||
AdditionalInheritedHandles []Handle // a list of additional handles, already marked as inheritable, that will be inherited by the new process
|
||||
ParentProcess Handle // if non-zero, the new process regards the process given by this handle as its parent process, and AdditionalInheritedHandles, if set, should exist in this parent process
|
||||
}
|
||||
|
||||
var zeroProcAttr ProcAttr
|
||||
var zeroSysProcAttr SysProcAttr
|
||||
|
||||
func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {
|
||||
if len(argv0) == 0 {
|
||||
return 0, 0, EWINDOWS
|
||||
}
|
||||
if attr == nil {
|
||||
attr = &zeroProcAttr
|
||||
}
|
||||
sys := attr.Sys
|
||||
if sys == nil {
|
||||
sys = &zeroSysProcAttr
|
||||
}
|
||||
|
||||
if len(attr.Files) > 3 {
|
||||
return 0, 0, EWINDOWS
|
||||
}
|
||||
if len(attr.Files) < 3 {
|
||||
return 0, 0, EINVAL
|
||||
}
|
||||
|
||||
if len(attr.Dir) != 0 {
|
||||
// StartProcess assumes that argv0 is relative to attr.Dir,
|
||||
// because it implies Chdir(attr.Dir) before executing argv0.
|
||||
// Windows CreateProcess assumes the opposite: it looks for
|
||||
// argv0 relative to the current directory, and, only once the new
|
||||
// process is started, it does Chdir(attr.Dir). We are adjusting
|
||||
// for that difference here by making argv0 absolute.
|
||||
var err error
|
||||
argv0, err = joinExeDirAndFName(attr.Dir, argv0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
argv0p, err := UTF16PtrFromString(argv0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
var cmdline string
|
||||
// Windows CreateProcess takes the command line as a single string:
|
||||
// use attr.CmdLine if set, else build the command line by escaping
|
||||
// and joining each argument with spaces
|
||||
if sys.CmdLine != "" {
|
||||
cmdline = sys.CmdLine
|
||||
} else {
|
||||
cmdline = makeCmdLine(argv)
|
||||
}
|
||||
|
||||
var argvp *uint16
|
||||
if len(cmdline) != 0 {
|
||||
argvp, err = UTF16PtrFromString(cmdline)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
var dirp *uint16
|
||||
if len(attr.Dir) != 0 {
|
||||
dirp, err = UTF16PtrFromString(attr.Dir)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
p, _ := GetCurrentProcess()
|
||||
parentProcess := p
|
||||
if sys.ParentProcess != 0 {
|
||||
parentProcess = sys.ParentProcess
|
||||
}
|
||||
fd := make([]Handle, len(attr.Files))
|
||||
for i := range attr.Files {
|
||||
if attr.Files[i] > 0 {
|
||||
err := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer DuplicateHandle(parentProcess, fd[i], 0, nil, 0, false, DUPLICATE_CLOSE_SOURCE)
|
||||
}
|
||||
}
|
||||
si := new(_STARTUPINFOEXW)
|
||||
si.ProcThreadAttributeList, err = newProcThreadAttributeList(2)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer deleteProcThreadAttributeList(si.ProcThreadAttributeList)
|
||||
si.Cb = uint32(unsafe.Sizeof(*si))
|
||||
si.Flags = STARTF_USESTDHANDLES
|
||||
if sys.HideWindow {
|
||||
si.Flags |= STARTF_USESHOWWINDOW
|
||||
si.ShowWindow = SW_HIDE
|
||||
}
|
||||
if sys.ParentProcess != 0 {
|
||||
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, unsafe.Pointer(&sys.ParentProcess), unsafe.Sizeof(sys.ParentProcess), nil, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
si.StdInput = fd[0]
|
||||
si.StdOutput = fd[1]
|
||||
si.StdErr = fd[2]
|
||||
|
||||
fd = append(fd, sys.AdditionalInheritedHandles...)
|
||||
|
||||
// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST
|
||||
// to treat the entire list as empty, so remove NULL handles.
|
||||
j := 0
|
||||
for i := range fd {
|
||||
if fd[i] != 0 {
|
||||
fd[j] = fd[i]
|
||||
j++
|
||||
}
|
||||
}
|
||||
fd = fd[:j]
|
||||
|
||||
willInheritHandles := len(fd) > 0 && !sys.NoInheritHandles
|
||||
|
||||
// Do not accidentally inherit more than these handles.
|
||||
if willInheritHandles {
|
||||
err = updateProcThreadAttribute(si.ProcThreadAttributeList, 0, _PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&fd[0]), uintptr(len(fd))*unsafe.Sizeof(fd[0]), nil, nil)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
|
||||
envBlock, err := createEnvBlock(attr.Env)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
pi := new(ProcessInformation)
|
||||
flags := sys.CreationFlags | CREATE_UNICODE_ENVIRONMENT | _EXTENDED_STARTUPINFO_PRESENT
|
||||
if sys.Token != 0 {
|
||||
err = CreateProcessAsUser(sys.Token, argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, &envBlock[0], dirp, &si.StartupInfo, pi)
|
||||
} else {
|
||||
err = CreateProcess(argv0p, argvp, sys.ProcessAttributes, sys.ThreadAttributes, willInheritHandles, flags, &envBlock[0], dirp, &si.StartupInfo, pi)
|
||||
}
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
defer CloseHandle(Handle(pi.Thread))
|
||||
runtime.KeepAlive(fd)
|
||||
runtime.KeepAlive(sys)
|
||||
|
||||
return int(pi.ProcessId), uintptr(pi.Process), nil
|
||||
}
|
||||
|
||||
func Exec(argv0 string, argv []string, envv []string) (err error) {
|
||||
return EWINDOWS
|
||||
}
|
||||
115
src/syscall/exec_windows_test.go
Normal file
115
src/syscall/exec_windows_test.go
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2020 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 syscall_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestEscapeArg(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input, output string
|
||||
}{
|
||||
{``, `""`},
|
||||
{`a`, `a`},
|
||||
{` `, `" "`},
|
||||
{`\`, `\`},
|
||||
{`"`, `\"`},
|
||||
{`\"`, `\\\"`},
|
||||
{`\\"`, `\\\\\"`},
|
||||
{`\\ `, `"\\ "`},
|
||||
{` \\`, `" \\\\"`},
|
||||
{`a `, `"a "`},
|
||||
{`C:\`, `C:\`},
|
||||
{`C:\Program Files (x32)\Common\`, `"C:\Program Files (x32)\Common\\"`},
|
||||
{`C:\Users\Игорь\`, `C:\Users\Игорь\`},
|
||||
{`Андрей\file`, `Андрей\file`},
|
||||
{`C:\Windows\temp`, `C:\Windows\temp`},
|
||||
{`c:\temp\newfile`, `c:\temp\newfile`},
|
||||
{`\\?\C:\Windows`, `\\?\C:\Windows`},
|
||||
{`\\?\`, `\\?\`},
|
||||
{`\\.\C:\Windows\`, `\\.\C:\Windows\`},
|
||||
{`\\server\share\file`, `\\server\share\file`},
|
||||
{`\\newserver\tempshare\really.txt`, `\\newserver\tempshare\really.txt`},
|
||||
}
|
||||
for _, test := range tests {
|
||||
if got := syscall.EscapeArg(test.input); got != test.output {
|
||||
t.Errorf("EscapeArg(%#q) = %#q, want %#q", test.input, got, test.output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangingProcessParent(t *testing.T) {
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "parent" {
|
||||
// in parent process
|
||||
|
||||
// Parent does nothing. It is just used as a parent of a child process.
|
||||
time.Sleep(time.Minute)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if os.Getenv("GO_WANT_HELPER_PROCESS") == "child" {
|
||||
// in child process
|
||||
dumpPath := os.Getenv("GO_WANT_HELPER_PROCESS_FILE")
|
||||
if dumpPath == "" {
|
||||
fmt.Fprintf(os.Stderr, "Dump file path cannot be blank.")
|
||||
os.Exit(1)
|
||||
}
|
||||
err := os.WriteFile(dumpPath, []byte(fmt.Sprintf("%d", os.Getppid())), 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error writing dump file: %v", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// run parent process
|
||||
|
||||
parent := exec.Command(os.Args[0], "-test.run=^TestChangingProcessParent$")
|
||||
parent.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=parent")
|
||||
err := parent.Start()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
parent.Process.Kill()
|
||||
parent.Wait()
|
||||
}()
|
||||
|
||||
// run child process
|
||||
|
||||
const _PROCESS_CREATE_PROCESS = 0x0080
|
||||
const _PROCESS_DUP_HANDLE = 0x0040
|
||||
childDumpPath := filepath.Join(t.TempDir(), "ppid.txt")
|
||||
ph, err := syscall.OpenProcess(_PROCESS_CREATE_PROCESS|_PROCESS_DUP_HANDLE|syscall.PROCESS_QUERY_INFORMATION,
|
||||
false, uint32(parent.Process.Pid))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer syscall.CloseHandle(ph)
|
||||
|
||||
child := exec.Command(os.Args[0], "-test.run=^TestChangingProcessParent$")
|
||||
child.Env = append(os.Environ(),
|
||||
"GO_WANT_HELPER_PROCESS=child",
|
||||
"GO_WANT_HELPER_PROCESS_FILE="+childDumpPath)
|
||||
child.SysProcAttr = &syscall.SysProcAttr{ParentProcess: ph}
|
||||
childOutput, err := child.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Errorf("child failed: %v: %v", err, string(childOutput))
|
||||
}
|
||||
childOutput, err = os.ReadFile(childDumpPath)
|
||||
if err != nil {
|
||||
t.Fatalf("reading child output failed: %v", err)
|
||||
}
|
||||
if got, want := string(childOutput), fmt.Sprintf("%d", parent.Process.Pid); got != want {
|
||||
t.Fatalf("child output: want %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
25
src/syscall/export_bsd_test.go
Normal file
25
src/syscall/export_bsd_test.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// pgid should really be pid_t, however _C_int (aka int32) is generally
|
||||
// equivalent.
|
||||
|
||||
func Tcgetpgrp(fd int) (pgid int32, err error) {
|
||||
if err := ioctlPtr(fd, TIOCGPGRP, unsafe.Pointer(&pgid)); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return pgid, nil
|
||||
}
|
||||
|
||||
func Tcsetpgrp(fd int, pgid int32) (err error) {
|
||||
return ioctlPtr(fd, TIOCSPGRP, unsafe.Pointer(&pgid))
|
||||
}
|
||||
35
src/syscall/export_linux_test.go
Normal file
35
src/syscall/export_linux_test.go
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2018 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 syscall
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
RawSyscallNoError = rawSyscallNoError
|
||||
ForceClone3 = &forceClone3
|
||||
Prlimit = prlimit
|
||||
)
|
||||
|
||||
const (
|
||||
Sys_GETEUID = sys_GETEUID
|
||||
)
|
||||
|
||||
func Tcgetpgrp(fd int) (pgid int32, err error) {
|
||||
_, _, errno := Syscall6(SYS_IOCTL, uintptr(fd), uintptr(TIOCGPGRP), uintptr(unsafe.Pointer(&pgid)), 0, 0, 0)
|
||||
if errno != 0 {
|
||||
return -1, errno
|
||||
}
|
||||
return pgid, nil
|
||||
}
|
||||
|
||||
func Tcsetpgrp(fd int, pgid int32) (err error) {
|
||||
_, _, errno := Syscall6(SYS_IOCTL, uintptr(fd), uintptr(TIOCSPGRP), uintptr(unsafe.Pointer(&pgid)), 0, 0, 0)
|
||||
if errno != 0 {
|
||||
return errno
|
||||
}
|
||||
return nil
|
||||
}
|
||||
17
src/syscall/export_rlimit_test.go
Normal file
17
src/syscall/export_rlimit_test.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2023 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"
|
||||
|
||||
func OrigRlimitNofile() *Rlimit {
|
||||
return origRlimitNofile.Load()
|
||||
}
|
||||
|
||||
func GetInternalOrigRlimitNofile() *atomic.Pointer[Rlimit] {
|
||||
return &origRlimitNofile
|
||||
}
|
||||
11
src/syscall/export_wasip1_test.go
Normal file
11
src/syscall/export_wasip1_test.go
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright 2023 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 wasip1
|
||||
|
||||
package syscall
|
||||
|
||||
func JoinPath(dir, file string) string {
|
||||
return joinPath(dir, file)
|
||||
}
|
||||
14
src/syscall/export_windows_test.go
Normal file
14
src/syscall/export_windows_test.go
Normal file
@@ -0,0 +1,14 @@
|
||||
// Copyright 2021 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 syscall
|
||||
|
||||
var NewProcThreadAttributeList = newProcThreadAttributeList
|
||||
var UpdateProcThreadAttribute = updateProcThreadAttribute
|
||||
var DeleteProcThreadAttributeList = deleteProcThreadAttributeList
|
||||
|
||||
const PROC_THREAD_ATTRIBUTE_HANDLE_LIST = _PROC_THREAD_ATTRIBUTE_HANDLE_LIST
|
||||
|
||||
var EncodeWTF16 = encodeWTF16
|
||||
var DecodeWTF16 = decodeWTF16
|
||||
18
src/syscall/flock_aix.go
Normal file
18
src/syscall/flock_aix.go
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2018 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 syscall
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// On AIX, there is no flock() system call.
|
||||
|
||||
// FcntlFlock performs a fcntl syscall for the [F_GETLK], [F_SETLK] or [F_SETLKW] command.
|
||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) (err error) {
|
||||
_, _, e1 := syscall6(uintptr(unsafe.Pointer(&libc_fcntl)), 3, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(lk)), 0, 0, 0)
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
}
|
||||
return
|
||||
}
|
||||
15
src/syscall/flock_bsd.go
Normal file
15
src/syscall/flock_bsd.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2018 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
|
||||
|
||||
package syscall
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// FcntlFlock performs a fcntl syscall for the [F_GETLK], [F_SETLK] or [F_SETLKW] command.
|
||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
||||
_, err := fcntlPtr(int(fd), cmd, unsafe.Pointer(lk))
|
||||
return err
|
||||
}
|
||||
20
src/syscall/flock_linux.go
Normal file
20
src/syscall/flock_linux.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2014 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 syscall
|
||||
|
||||
import "unsafe"
|
||||
|
||||
// fcntl64Syscall is usually SYS_FCNTL, but is overridden on 32-bit Linux
|
||||
// systems by flock_linux_32bit.go to be SYS_FCNTL64.
|
||||
var fcntl64Syscall uintptr = SYS_FCNTL
|
||||
|
||||
// FcntlFlock performs a fcntl syscall for the [F_GETLK], [F_SETLK] or [F_SETLKW] command.
|
||||
func FcntlFlock(fd uintptr, cmd int, lk *Flock_t) error {
|
||||
_, _, errno := Syscall(fcntl64Syscall, fd, uintptr(cmd), uintptr(unsafe.Pointer(lk)))
|
||||
if errno == 0 {
|
||||
return nil
|
||||
}
|
||||
return errno
|
||||
}
|
||||
13
src/syscall/flock_linux_32bit.go
Normal file
13
src/syscall/flock_linux_32bit.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2014 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 && 386) || (linux && arm) || (linux && mips) || (linux && mipsle)
|
||||
|
||||
package syscall
|
||||
|
||||
func init() {
|
||||
// On 32-bit Linux systems, the fcntl syscall that matches Go's
|
||||
// Flock_t type is SYS_FCNTL64, not SYS_FCNTL.
|
||||
fcntl64Syscall = SYS_FCNTL64
|
||||
}
|
||||
30
src/syscall/forkpipe.go
Normal file
30
src/syscall/forkpipe.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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
|
||||
|
||||
// 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
|
||||
}
|
||||
_, err = fcntl(p[0], F_SETFD, FD_CLOEXEC)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fcntl(p[1], F_SETFD, FD_CLOEXEC)
|
||||
return err
|
||||
}
|
||||
|
||||
func acquireForkLock() {
|
||||
ForkLock.Lock()
|
||||
}
|
||||
|
||||
func releaseForkLock() {
|
||||
ForkLock.Unlock()
|
||||
}
|
||||
98
src/syscall/forkpipe2.go
Normal file
98
src/syscall/forkpipe2.go
Normal file
@@ -0,0 +1,98 @@
|
||||
// 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"
|
||||
|
||||
// forkExecPipe atomically opens a pipe with O_CLOEXEC set on both file
|
||||
// descriptors.
|
||||
func forkExecPipe(p []int) error {
|
||||
return Pipe2(p, 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
|
||||
)
|
||||
|
||||
// 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
|
||||
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
568
src/syscall/fs_js.go
Normal file
568
src/syscall/fs_js.go
Normal file
@@ -0,0 +1,568 @@
|
||||
// Copyright 2018 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 js && wasm
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
// Provided by package runtime.
|
||||
func now() (sec int64, nsec int32)
|
||||
|
||||
var jsProcess = js.Global().Get("process")
|
||||
var jsFS = js.Global().Get("fs")
|
||||
var constants = jsFS.Get("constants")
|
||||
|
||||
var uint8Array = js.Global().Get("Uint8Array")
|
||||
|
||||
var (
|
||||
nodeWRONLY = constants.Get("O_WRONLY").Int()
|
||||
nodeRDWR = constants.Get("O_RDWR").Int()
|
||||
nodeCREATE = constants.Get("O_CREAT").Int()
|
||||
nodeTRUNC = constants.Get("O_TRUNC").Int()
|
||||
nodeAPPEND = constants.Get("O_APPEND").Int()
|
||||
nodeEXCL = constants.Get("O_EXCL").Int()
|
||||
)
|
||||
|
||||
type jsFile struct {
|
||||
path string
|
||||
entries []string
|
||||
dirIdx int // entries[:dirIdx] have already been returned in ReadDirent
|
||||
pos int64
|
||||
seeked bool
|
||||
}
|
||||
|
||||
var filesMu sync.Mutex
|
||||
var files = map[int]*jsFile{
|
||||
0: {},
|
||||
1: {},
|
||||
2: {},
|
||||
}
|
||||
|
||||
func fdToFile(fd int) (*jsFile, error) {
|
||||
filesMu.Lock()
|
||||
f, ok := files[fd]
|
||||
filesMu.Unlock()
|
||||
if !ok {
|
||||
return nil, EBADF
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func Open(path string, openmode int, perm uint32) (int, error) {
|
||||
if err := checkPath(path); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
flags := 0
|
||||
if openmode&O_WRONLY != 0 {
|
||||
flags |= nodeWRONLY
|
||||
}
|
||||
if openmode&O_RDWR != 0 {
|
||||
flags |= nodeRDWR
|
||||
}
|
||||
if openmode&O_CREATE != 0 {
|
||||
flags |= nodeCREATE
|
||||
}
|
||||
if openmode&O_TRUNC != 0 {
|
||||
flags |= nodeTRUNC
|
||||
}
|
||||
if openmode&O_APPEND != 0 {
|
||||
flags |= nodeAPPEND
|
||||
}
|
||||
if openmode&O_EXCL != 0 {
|
||||
flags |= nodeEXCL
|
||||
}
|
||||
if openmode&O_SYNC != 0 {
|
||||
return 0, errors.New("syscall.Open: O_SYNC is not supported by js/wasm")
|
||||
}
|
||||
|
||||
jsFD, err := fsCall("open", path, flags, perm)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
fd := jsFD.Int()
|
||||
|
||||
var entries []string
|
||||
if stat, err := fsCall("fstat", fd); err == nil && stat.Call("isDirectory").Bool() {
|
||||
dir, err := fsCall("readdir", path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
entries = make([]string, dir.Length())
|
||||
for i := range entries {
|
||||
entries[i] = dir.Index(i).String()
|
||||
}
|
||||
}
|
||||
|
||||
if path[0] != '/' {
|
||||
cwd := jsProcess.Call("cwd").String()
|
||||
path = cwd + "/" + path
|
||||
}
|
||||
f := &jsFile{
|
||||
path: path,
|
||||
entries: entries,
|
||||
}
|
||||
filesMu.Lock()
|
||||
files[fd] = f
|
||||
filesMu.Unlock()
|
||||
return fd, nil
|
||||
}
|
||||
|
||||
func Close(fd int) error {
|
||||
filesMu.Lock()
|
||||
delete(files, fd)
|
||||
filesMu.Unlock()
|
||||
_, err := fsCall("close", fd)
|
||||
return err
|
||||
}
|
||||
|
||||
func CloseOnExec(fd int) {
|
||||
// nothing to do - no exec
|
||||
}
|
||||
|
||||
func Mkdir(path string, perm uint32) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("mkdir", path, perm)
|
||||
return err
|
||||
}
|
||||
|
||||
func ReadDirent(fd int, buf []byte) (int, error) {
|
||||
f, err := fdToFile(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if f.entries == nil {
|
||||
return 0, EINVAL
|
||||
}
|
||||
|
||||
n := 0
|
||||
for f.dirIdx < len(f.entries) {
|
||||
entry := f.entries[f.dirIdx]
|
||||
l := 2 + len(entry)
|
||||
if l > len(buf) {
|
||||
break
|
||||
}
|
||||
buf[0] = byte(l)
|
||||
buf[1] = byte(l >> 8)
|
||||
copy(buf[2:], entry)
|
||||
buf = buf[l:]
|
||||
n += l
|
||||
f.dirIdx++
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func setStat(st *Stat_t, jsSt js.Value) {
|
||||
st.Dev = int64(jsSt.Get("dev").Int())
|
||||
st.Ino = uint64(jsSt.Get("ino").Int())
|
||||
st.Mode = uint32(jsSt.Get("mode").Int())
|
||||
st.Nlink = uint32(jsSt.Get("nlink").Int())
|
||||
st.Uid = uint32(jsSt.Get("uid").Int())
|
||||
st.Gid = uint32(jsSt.Get("gid").Int())
|
||||
st.Rdev = int64(jsSt.Get("rdev").Int())
|
||||
st.Size = int64(jsSt.Get("size").Int())
|
||||
st.Blksize = int32(jsSt.Get("blksize").Int())
|
||||
st.Blocks = int32(jsSt.Get("blocks").Int())
|
||||
atime := int64(jsSt.Get("atimeMs").Int())
|
||||
st.Atime = atime / 1000
|
||||
st.AtimeNsec = (atime % 1000) * 1000000
|
||||
mtime := int64(jsSt.Get("mtimeMs").Int())
|
||||
st.Mtime = mtime / 1000
|
||||
st.MtimeNsec = (mtime % 1000) * 1000000
|
||||
ctime := int64(jsSt.Get("ctimeMs").Int())
|
||||
st.Ctime = ctime / 1000
|
||||
st.CtimeNsec = (ctime % 1000) * 1000000
|
||||
}
|
||||
|
||||
func Stat(path string, st *Stat_t) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
jsSt, err := fsCall("stat", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setStat(st, jsSt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Lstat(path string, st *Stat_t) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
jsSt, err := fsCall("lstat", path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setStat(st, jsSt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Fstat(fd int, st *Stat_t) error {
|
||||
jsSt, err := fsCall("fstat", fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
setStat(st, jsSt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Unlink(path string) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("unlink", path)
|
||||
return err
|
||||
}
|
||||
|
||||
func Rmdir(path string) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("rmdir", path)
|
||||
return err
|
||||
}
|
||||
|
||||
func Chmod(path string, mode uint32) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("chmod", path, mode)
|
||||
return err
|
||||
}
|
||||
|
||||
func Fchmod(fd int, mode uint32) error {
|
||||
_, err := fsCall("fchmod", fd, mode)
|
||||
return err
|
||||
}
|
||||
|
||||
func Chown(path string, uid, gid int) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("chown", path, uint32(uid), uint32(gid))
|
||||
return err
|
||||
}
|
||||
|
||||
func Fchown(fd int, uid, gid int) error {
|
||||
_, err := fsCall("fchown", fd, uint32(uid), uint32(gid))
|
||||
return err
|
||||
}
|
||||
|
||||
func Lchown(path string, uid, gid int) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
if jsFS.Get("lchown").IsUndefined() {
|
||||
// fs.lchown is unavailable on Linux until Node.js 10.6.0
|
||||
// TODO(neelance): remove when we require at least this Node.js version
|
||||
return ENOSYS
|
||||
}
|
||||
_, err := fsCall("lchown", path, uint32(uid), uint32(gid))
|
||||
return err
|
||||
}
|
||||
|
||||
func UtimesNano(path string, ts []Timespec) error {
|
||||
// UTIME_OMIT value must match internal/syscall/unix/at_js.go
|
||||
const UTIME_OMIT = -0x2
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ts) != 2 {
|
||||
return EINVAL
|
||||
}
|
||||
atime := ts[0].Sec
|
||||
mtime := ts[1].Sec
|
||||
if atime == UTIME_OMIT || mtime == UTIME_OMIT {
|
||||
var st Stat_t
|
||||
if err := Stat(path, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
if atime == UTIME_OMIT {
|
||||
atime = st.Atime
|
||||
}
|
||||
if mtime == UTIME_OMIT {
|
||||
mtime = st.Mtime
|
||||
}
|
||||
}
|
||||
_, err := fsCall("utimes", path, atime, mtime)
|
||||
return err
|
||||
}
|
||||
|
||||
func Rename(from, to string) error {
|
||||
if err := checkPath(from); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkPath(to); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("rename", from, to)
|
||||
return err
|
||||
}
|
||||
|
||||
func Truncate(path string, length int64) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("truncate", path, length)
|
||||
return err
|
||||
}
|
||||
|
||||
func Ftruncate(fd int, length int64) error {
|
||||
_, err := fsCall("ftruncate", fd, length)
|
||||
return err
|
||||
}
|
||||
|
||||
func Getcwd(buf []byte) (n int, err error) {
|
||||
defer recoverErr(&err)
|
||||
cwd := jsProcess.Call("cwd").String()
|
||||
n = copy(buf, cwd)
|
||||
return
|
||||
}
|
||||
|
||||
func Chdir(path string) (err error) {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
defer recoverErr(&err)
|
||||
jsProcess.Call("chdir", path)
|
||||
return
|
||||
}
|
||||
|
||||
func Fchdir(fd int) error {
|
||||
f, err := fdToFile(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return Chdir(f.path)
|
||||
}
|
||||
|
||||
func Readlink(path string, buf []byte) (n int, err error) {
|
||||
if err := checkPath(path); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dst, err := fsCall("readlink", path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n = copy(buf, dst.String())
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func Link(path, link string) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkPath(link); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("link", path, link)
|
||||
return err
|
||||
}
|
||||
|
||||
func Symlink(path, link string) error {
|
||||
if err := checkPath(path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := checkPath(link); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := fsCall("symlink", path, link)
|
||||
return err
|
||||
}
|
||||
|
||||
func Fsync(fd int) error {
|
||||
_, err := fsCall("fsync", fd)
|
||||
return err
|
||||
}
|
||||
|
||||
func Read(fd int, b []byte) (int, error) {
|
||||
f, err := fdToFile(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if f.seeked {
|
||||
n, err := Pread(fd, b, f.pos)
|
||||
f.pos += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
buf := uint8Array.New(len(b))
|
||||
n, err := fsCall("read", fd, buf, 0, len(b), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
js.CopyBytesToGo(b, buf)
|
||||
|
||||
n2 := n.Int()
|
||||
f.pos += int64(n2)
|
||||
return n2, err
|
||||
}
|
||||
|
||||
func Write(fd int, b []byte) (int, error) {
|
||||
f, err := fdToFile(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if f.seeked {
|
||||
n, err := Pwrite(fd, b, f.pos)
|
||||
f.pos += int64(n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
if faketime && (fd == 1 || fd == 2) {
|
||||
n := faketimeWrite(fd, b)
|
||||
if n < 0 {
|
||||
return 0, errnoErr(Errno(-n))
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
buf := uint8Array.New(len(b))
|
||||
js.CopyBytesToJS(buf, b)
|
||||
n, err := fsCall("write", fd, buf, 0, len(b), nil)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n2 := n.Int()
|
||||
f.pos += int64(n2)
|
||||
return n2, err
|
||||
}
|
||||
|
||||
func Pread(fd int, b []byte, offset int64) (int, error) {
|
||||
buf := uint8Array.New(len(b))
|
||||
n, err := fsCall("read", fd, buf, 0, len(b), offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
js.CopyBytesToGo(b, buf)
|
||||
return n.Int(), nil
|
||||
}
|
||||
|
||||
func Pwrite(fd int, b []byte, offset int64) (int, error) {
|
||||
buf := uint8Array.New(len(b))
|
||||
js.CopyBytesToJS(buf, b)
|
||||
n, err := fsCall("write", fd, buf, 0, len(b), offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return n.Int(), nil
|
||||
}
|
||||
|
||||
func Seek(fd int, offset int64, whence int) (int64, error) {
|
||||
f, err := fdToFile(fd)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var newPos int64
|
||||
switch whence {
|
||||
case 0:
|
||||
newPos = offset
|
||||
case 1:
|
||||
newPos = f.pos + offset
|
||||
case 2:
|
||||
var st Stat_t
|
||||
if err := Fstat(fd, &st); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
newPos = st.Size + offset
|
||||
default:
|
||||
return 0, errnoErr(EINVAL)
|
||||
}
|
||||
|
||||
if newPos < 0 {
|
||||
return 0, errnoErr(EINVAL)
|
||||
}
|
||||
|
||||
f.seeked = true
|
||||
f.dirIdx = 0 // Reset directory read position. See issue 35767.
|
||||
f.pos = newPos
|
||||
return newPos, nil
|
||||
}
|
||||
|
||||
func Dup(fd int) (int, error) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
func Dup2(fd, newfd int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func Pipe(fd []int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func fsCall(name string, args ...any) (js.Value, error) {
|
||||
type callResult struct {
|
||||
val js.Value
|
||||
err error
|
||||
}
|
||||
|
||||
c := make(chan callResult, 1)
|
||||
f := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
var res callResult
|
||||
|
||||
if len(args) >= 1 { // on Node.js 8, fs.utimes calls the callback without any arguments
|
||||
if jsErr := args[0]; !jsErr.IsNull() {
|
||||
res.err = mapJSError(jsErr)
|
||||
}
|
||||
}
|
||||
|
||||
res.val = js.Undefined()
|
||||
if len(args) >= 2 {
|
||||
res.val = args[1]
|
||||
}
|
||||
|
||||
c <- res
|
||||
return nil
|
||||
})
|
||||
defer f.Release()
|
||||
jsFS.Call(name, append(args, f)...)
|
||||
res := <-c
|
||||
return res.val, res.err
|
||||
}
|
||||
|
||||
// checkPath checks that the path is not empty and that it contains no null characters.
|
||||
func checkPath(path string) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
for i := 0; i < len(path); i++ {
|
||||
if path[i] == '\x00' {
|
||||
return EINVAL
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func recoverErr(errPtr *error) {
|
||||
if err := recover(); err != nil {
|
||||
jsErr, ok := err.(js.Error)
|
||||
if !ok {
|
||||
panic(err)
|
||||
}
|
||||
*errPtr = mapJSError(jsErr.Value)
|
||||
}
|
||||
}
|
||||
|
||||
// mapJSError maps an error given by Node.js to the appropriate Go error.
|
||||
func mapJSError(jsErr js.Value) error {
|
||||
errno, ok := errnoByCode[jsErr.Get("code").String()]
|
||||
if !ok {
|
||||
panic(jsErr)
|
||||
}
|
||||
return errnoErr(Errno(errno))
|
||||
}
|
||||
932
src/syscall/fs_wasip1.go
Normal file
932
src/syscall/fs_wasip1.go
Normal file
@@ -0,0 +1,932 @@
|
||||
// Copyright 2023 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 wasip1
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"internal/stringslite"
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Try to set stdio to non-blocking mode before the os package
|
||||
// calls NewFile for each fd. NewFile queries the non-blocking flag
|
||||
// but doesn't change it, even if the runtime supports non-blocking
|
||||
// stdio. Since WebAssembly modules are single-threaded, blocking
|
||||
// system calls temporarily halt execution of the module. If the
|
||||
// runtime supports non-blocking stdio, the Go runtime is able to
|
||||
// use the WASI net poller to poll for read/write readiness and is
|
||||
// able to schedule goroutines while waiting.
|
||||
SetNonblock(0, true)
|
||||
SetNonblock(1, true)
|
||||
SetNonblock(2, true)
|
||||
}
|
||||
|
||||
type uintptr32 = uint32
|
||||
type size = uint32
|
||||
type fdflags = uint32
|
||||
type filesize = uint64
|
||||
type filetype = uint8
|
||||
type lookupflags = uint32
|
||||
type oflags = uint32
|
||||
type rights = uint64
|
||||
type timestamp = uint64
|
||||
type dircookie = uint64
|
||||
type filedelta = int64
|
||||
type fstflags = uint32
|
||||
|
||||
type iovec struct {
|
||||
buf uintptr32
|
||||
bufLen size
|
||||
}
|
||||
|
||||
const (
|
||||
LOOKUP_SYMLINK_FOLLOW = 0x00000001
|
||||
)
|
||||
|
||||
const (
|
||||
OFLAG_CREATE = 0x0001
|
||||
OFLAG_DIRECTORY = 0x0002
|
||||
OFLAG_EXCL = 0x0004
|
||||
OFLAG_TRUNC = 0x0008
|
||||
)
|
||||
|
||||
const (
|
||||
FDFLAG_APPEND = 0x0001
|
||||
FDFLAG_DSYNC = 0x0002
|
||||
FDFLAG_NONBLOCK = 0x0004
|
||||
FDFLAG_RSYNC = 0x0008
|
||||
FDFLAG_SYNC = 0x0010
|
||||
)
|
||||
|
||||
const (
|
||||
RIGHT_FD_DATASYNC = 1 << iota
|
||||
RIGHT_FD_READ
|
||||
RIGHT_FD_SEEK
|
||||
RIGHT_FDSTAT_SET_FLAGS
|
||||
RIGHT_FD_SYNC
|
||||
RIGHT_FD_TELL
|
||||
RIGHT_FD_WRITE
|
||||
RIGHT_FD_ADVISE
|
||||
RIGHT_FD_ALLOCATE
|
||||
RIGHT_PATH_CREATE_DIRECTORY
|
||||
RIGHT_PATH_CREATE_FILE
|
||||
RIGHT_PATH_LINK_SOURCE
|
||||
RIGHT_PATH_LINK_TARGET
|
||||
RIGHT_PATH_OPEN
|
||||
RIGHT_FD_READDIR
|
||||
RIGHT_PATH_READLINK
|
||||
RIGHT_PATH_RENAME_SOURCE
|
||||
RIGHT_PATH_RENAME_TARGET
|
||||
RIGHT_PATH_FILESTAT_GET
|
||||
RIGHT_PATH_FILESTAT_SET_SIZE
|
||||
RIGHT_PATH_FILESTAT_SET_TIMES
|
||||
RIGHT_FD_FILESTAT_GET
|
||||
RIGHT_FD_FILESTAT_SET_SIZE
|
||||
RIGHT_FD_FILESTAT_SET_TIMES
|
||||
RIGHT_PATH_SYMLINK
|
||||
RIGHT_PATH_REMOVE_DIRECTORY
|
||||
RIGHT_PATH_UNLINK_FILE
|
||||
RIGHT_POLL_FD_READWRITE
|
||||
RIGHT_SOCK_SHUTDOWN
|
||||
RIGHT_SOCK_ACCEPT
|
||||
)
|
||||
|
||||
const (
|
||||
WHENCE_SET = 0
|
||||
WHENCE_CUR = 1
|
||||
WHENCE_END = 2
|
||||
)
|
||||
|
||||
const (
|
||||
FILESTAT_SET_ATIM = 0x0001
|
||||
FILESTAT_SET_ATIM_NOW = 0x0002
|
||||
FILESTAT_SET_MTIM = 0x0004
|
||||
FILESTAT_SET_MTIM_NOW = 0x0008
|
||||
)
|
||||
|
||||
const (
|
||||
// Despite the rights being defined as a 64 bits integer in the spec,
|
||||
// wasmtime crashes the program if we set any of the upper 32 bits.
|
||||
fullRights = rights(^uint32(0))
|
||||
readRights = rights(RIGHT_FD_READ | RIGHT_FD_READDIR)
|
||||
writeRights = rights(RIGHT_FD_DATASYNC | RIGHT_FD_WRITE | RIGHT_FD_ALLOCATE | RIGHT_PATH_FILESTAT_SET_SIZE)
|
||||
|
||||
// Some runtimes have very strict expectations when it comes to which
|
||||
// rights can be enabled on files opened by path_open. The fileRights
|
||||
// constant is used as a mask to retain only bits for operations that
|
||||
// are supported on files.
|
||||
fileRights rights = RIGHT_FD_DATASYNC |
|
||||
RIGHT_FD_READ |
|
||||
RIGHT_FD_SEEK |
|
||||
RIGHT_FDSTAT_SET_FLAGS |
|
||||
RIGHT_FD_SYNC |
|
||||
RIGHT_FD_TELL |
|
||||
RIGHT_FD_WRITE |
|
||||
RIGHT_FD_ADVISE |
|
||||
RIGHT_FD_ALLOCATE |
|
||||
RIGHT_PATH_CREATE_DIRECTORY |
|
||||
RIGHT_PATH_CREATE_FILE |
|
||||
RIGHT_PATH_LINK_SOURCE |
|
||||
RIGHT_PATH_LINK_TARGET |
|
||||
RIGHT_PATH_OPEN |
|
||||
RIGHT_FD_READDIR |
|
||||
RIGHT_PATH_READLINK |
|
||||
RIGHT_PATH_RENAME_SOURCE |
|
||||
RIGHT_PATH_RENAME_TARGET |
|
||||
RIGHT_PATH_FILESTAT_GET |
|
||||
RIGHT_PATH_FILESTAT_SET_SIZE |
|
||||
RIGHT_PATH_FILESTAT_SET_TIMES |
|
||||
RIGHT_FD_FILESTAT_GET |
|
||||
RIGHT_FD_FILESTAT_SET_SIZE |
|
||||
RIGHT_FD_FILESTAT_SET_TIMES |
|
||||
RIGHT_PATH_SYMLINK |
|
||||
RIGHT_PATH_REMOVE_DIRECTORY |
|
||||
RIGHT_PATH_UNLINK_FILE |
|
||||
RIGHT_POLL_FD_READWRITE
|
||||
|
||||
// Runtimes like wasmtime and wasmedge will refuse to open directories
|
||||
// if the rights requested by the application exceed the operations that
|
||||
// can be performed on a directory.
|
||||
dirRights rights = RIGHT_FD_SEEK |
|
||||
RIGHT_FDSTAT_SET_FLAGS |
|
||||
RIGHT_FD_SYNC |
|
||||
RIGHT_PATH_CREATE_DIRECTORY |
|
||||
RIGHT_PATH_CREATE_FILE |
|
||||
RIGHT_PATH_LINK_SOURCE |
|
||||
RIGHT_PATH_LINK_TARGET |
|
||||
RIGHT_PATH_OPEN |
|
||||
RIGHT_FD_READDIR |
|
||||
RIGHT_PATH_READLINK |
|
||||
RIGHT_PATH_RENAME_SOURCE |
|
||||
RIGHT_PATH_RENAME_TARGET |
|
||||
RIGHT_PATH_FILESTAT_GET |
|
||||
RIGHT_PATH_FILESTAT_SET_SIZE |
|
||||
RIGHT_PATH_FILESTAT_SET_TIMES |
|
||||
RIGHT_FD_FILESTAT_GET |
|
||||
RIGHT_FD_FILESTAT_SET_TIMES |
|
||||
RIGHT_PATH_SYMLINK |
|
||||
RIGHT_PATH_REMOVE_DIRECTORY |
|
||||
RIGHT_PATH_UNLINK_FILE
|
||||
)
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_closefd-fd---result-errno
|
||||
//
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_close
|
||||
//go:noescape
|
||||
func fd_close(fd int32) Errno
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_filestat_set_sizefd-fd-size-filesize---result-errno
|
||||
//
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_filestat_set_size
|
||||
//go:noescape
|
||||
func fd_filestat_set_size(fd int32, set_size filesize) Errno
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_preadfd-fd-iovs-iovec_array-offset-filesize---resultsize-errno
|
||||
//
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_pread
|
||||
//go:noescape
|
||||
func fd_pread(fd int32, iovs unsafe.Pointer, iovsLen size, offset filesize, nread unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_pwrite
|
||||
//go:noescape
|
||||
func fd_pwrite(fd int32, iovs unsafe.Pointer, iovsLen size, offset filesize, nwritten unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_read
|
||||
//go:noescape
|
||||
func fd_read(fd int32, iovs unsafe.Pointer, iovsLen size, nread unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_readdir
|
||||
//go:noescape
|
||||
func fd_readdir(fd int32, buf unsafe.Pointer, bufLen size, cookie dircookie, nwritten unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_seek
|
||||
//go:noescape
|
||||
func fd_seek(fd int32, offset filedelta, whence uint32, newoffset unsafe.Pointer) Errno
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fd_fdstat_set_rightsfd-fd-fs_rights_base-rights-fs_rights_inheriting-rights---result-errno
|
||||
//
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_rights
|
||||
//go:noescape
|
||||
func fd_fdstat_set_rights(fd int32, rightsBase rights, rightsInheriting rights) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_filestat_get
|
||||
//go:noescape
|
||||
func fd_filestat_get(fd int32, buf unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_write
|
||||
//go:noescape
|
||||
func fd_write(fd int32, iovs unsafe.Pointer, iovsLen size, nwritten unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_sync
|
||||
//go:noescape
|
||||
func fd_sync(fd int32) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_create_directory
|
||||
//go:noescape
|
||||
func path_create_directory(fd int32, path unsafe.Pointer, pathLen size) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_filestat_get
|
||||
//go:noescape
|
||||
func path_filestat_get(fd int32, flags lookupflags, path unsafe.Pointer, pathLen size, buf unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_filestat_set_times
|
||||
//go:noescape
|
||||
func path_filestat_set_times(fd int32, flags lookupflags, path unsafe.Pointer, pathLen size, atim timestamp, mtim timestamp, fstflags fstflags) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_link
|
||||
//go:noescape
|
||||
func path_link(oldFd int32, oldFlags lookupflags, oldPath unsafe.Pointer, oldPathLen size, newFd int32, newPath unsafe.Pointer, newPathLen size) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_readlink
|
||||
//go:noescape
|
||||
func path_readlink(fd int32, path unsafe.Pointer, pathLen size, buf unsafe.Pointer, bufLen size, nwritten unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_remove_directory
|
||||
//go:noescape
|
||||
func path_remove_directory(fd int32, path unsafe.Pointer, pathLen size) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_rename
|
||||
//go:noescape
|
||||
func path_rename(oldFd int32, oldPath unsafe.Pointer, oldPathLen size, newFd int32, newPath unsafe.Pointer, newPathLen size) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_symlink
|
||||
//go:noescape
|
||||
func path_symlink(oldPath unsafe.Pointer, oldPathLen size, fd int32, newPath unsafe.Pointer, newPathLen size) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_unlink_file
|
||||
//go:noescape
|
||||
func path_unlink_file(fd int32, path unsafe.Pointer, pathLen size) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 path_open
|
||||
//go:noescape
|
||||
func path_open(rootFD int32, dirflags lookupflags, path unsafe.Pointer, pathLen size, oflags oflags, fsRightsBase rights, fsRightsInheriting rights, fsFlags fdflags, fd unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 random_get
|
||||
//go:noescape
|
||||
func random_get(buf unsafe.Pointer, bufLen size) Errno
|
||||
|
||||
// https://github.com/WebAssembly/WASI/blob/a2b96e81c0586125cc4dc79a5be0b78d9a059925/legacy/preview1/docs.md#-fdstat-record
|
||||
// fdflags must be at offset 2, hence the uint16 type rather than the
|
||||
// fdflags (uint32) type.
|
||||
type fdstat struct {
|
||||
filetype filetype
|
||||
fdflags uint16
|
||||
rightsBase rights
|
||||
rightsInheriting rights
|
||||
}
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_fdstat_get
|
||||
//go:noescape
|
||||
func fd_fdstat_get(fd int32, buf unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_fdstat_set_flags
|
||||
//go:noescape
|
||||
func fd_fdstat_set_flags(fd int32, flags fdflags) Errno
|
||||
|
||||
// fd_fdstat_get_flags is accessed from internal/syscall/unix
|
||||
//go:linkname fd_fdstat_get_flags
|
||||
|
||||
func fd_fdstat_get_flags(fd int) (uint32, error) {
|
||||
var stat fdstat
|
||||
errno := fd_fdstat_get(int32(fd), unsafe.Pointer(&stat))
|
||||
return uint32(stat.fdflags), errnoErr(errno)
|
||||
}
|
||||
|
||||
// fd_fdstat_get_type is accessed from net
|
||||
//go:linkname fd_fdstat_get_type
|
||||
|
||||
func fd_fdstat_get_type(fd int) (uint8, error) {
|
||||
var stat fdstat
|
||||
errno := fd_fdstat_get(int32(fd), unsafe.Pointer(&stat))
|
||||
return stat.filetype, errnoErr(errno)
|
||||
}
|
||||
|
||||
type preopentype = uint8
|
||||
|
||||
const (
|
||||
preopentypeDir preopentype = iota
|
||||
)
|
||||
|
||||
type prestatDir struct {
|
||||
prNameLen size
|
||||
}
|
||||
|
||||
type prestat struct {
|
||||
typ preopentype
|
||||
dir prestatDir
|
||||
}
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_prestat_get
|
||||
//go:noescape
|
||||
func fd_prestat_get(fd int32, prestat unsafe.Pointer) Errno
|
||||
|
||||
//go:wasmimport wasi_snapshot_preview1 fd_prestat_dir_name
|
||||
//go:noescape
|
||||
func fd_prestat_dir_name(fd int32, path unsafe.Pointer, pathLen size) Errno
|
||||
|
||||
type opendir struct {
|
||||
fd int32
|
||||
name string
|
||||
}
|
||||
|
||||
// List of preopen directories that were exposed by the runtime. The first one
|
||||
// is assumed to the be root directory of the file system, and others are seen
|
||||
// as mount points at sub paths of the root.
|
||||
var preopens []opendir
|
||||
|
||||
// Current working directory. We maintain this as a string and resolve paths in
|
||||
// the code because wasmtime does not allow relative path lookups outside of the
|
||||
// scope of a directory; a previous approach we tried consisted in maintaining
|
||||
// open a file descriptor to the current directory so we could perform relative
|
||||
// path lookups from that location, but it resulted in breaking path resolution
|
||||
// from the current directory to its parent.
|
||||
var cwd string
|
||||
|
||||
func init() {
|
||||
dirNameBuf := make([]byte, 256)
|
||||
// We start looking for preopens at fd=3 because 0, 1, and 2 are reserved
|
||||
// for standard input and outputs.
|
||||
for preopenFd := int32(3); ; preopenFd++ {
|
||||
var prestat prestat
|
||||
|
||||
errno := fd_prestat_get(preopenFd, unsafe.Pointer(&prestat))
|
||||
if errno == EBADF {
|
||||
break
|
||||
}
|
||||
if errno == ENOTDIR || prestat.typ != preopentypeDir {
|
||||
continue
|
||||
}
|
||||
if errno != 0 {
|
||||
panic("fd_prestat: " + errno.Error())
|
||||
}
|
||||
if int(prestat.dir.prNameLen) > len(dirNameBuf) {
|
||||
dirNameBuf = make([]byte, prestat.dir.prNameLen)
|
||||
}
|
||||
|
||||
errno = fd_prestat_dir_name(preopenFd, unsafe.Pointer(&dirNameBuf[0]), prestat.dir.prNameLen)
|
||||
if errno != 0 {
|
||||
panic("fd_prestat_dir_name: " + errno.Error())
|
||||
}
|
||||
|
||||
preopens = append(preopens, opendir{
|
||||
fd: preopenFd,
|
||||
name: string(dirNameBuf[:prestat.dir.prNameLen]),
|
||||
})
|
||||
}
|
||||
|
||||
if cwd, _ = Getenv("PWD"); cwd != "" {
|
||||
cwd = joinPath("/", cwd)
|
||||
} else if len(preopens) > 0 {
|
||||
cwd = preopens[0].name
|
||||
}
|
||||
}
|
||||
|
||||
// Provided by package runtime.
|
||||
func now() (sec int64, nsec int32)
|
||||
|
||||
//go:nosplit
|
||||
func appendCleanPath(buf []byte, path string, lookupParent bool) ([]byte, bool) {
|
||||
i := 0
|
||||
for i < len(path) {
|
||||
for i < len(path) && path[i] == '/' {
|
||||
i++
|
||||
}
|
||||
|
||||
j := i
|
||||
for j < len(path) && path[j] != '/' {
|
||||
j++
|
||||
}
|
||||
|
||||
s := path[i:j]
|
||||
i = j
|
||||
|
||||
switch s {
|
||||
case "":
|
||||
continue
|
||||
case ".":
|
||||
continue
|
||||
case "..":
|
||||
if !lookupParent {
|
||||
k := len(buf)
|
||||
for k > 0 && buf[k-1] != '/' {
|
||||
k--
|
||||
}
|
||||
for k > 1 && buf[k-1] == '/' {
|
||||
k--
|
||||
}
|
||||
buf = buf[:k]
|
||||
if k == 0 {
|
||||
lookupParent = true
|
||||
} else {
|
||||
s = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
default:
|
||||
lookupParent = false
|
||||
}
|
||||
|
||||
if len(buf) > 0 && buf[len(buf)-1] != '/' {
|
||||
buf = append(buf, '/')
|
||||
}
|
||||
buf = append(buf, s...)
|
||||
}
|
||||
return buf, lookupParent
|
||||
}
|
||||
|
||||
// joinPath concatenates dir and file paths, producing a cleaned path where
|
||||
// "." and ".." have been removed, unless dir is relative and the references
|
||||
// to parent directories in file represented a location relative to a parent
|
||||
// of dir.
|
||||
//
|
||||
// This function is used for path resolution of all wasi functions expecting
|
||||
// a path argument; the returned string is heap allocated, which we may want
|
||||
// to optimize in the future. Instead of returning a string, the function
|
||||
// could append the result to an output buffer that the functions in this
|
||||
// file can manage to have allocated on the stack (e.g. initializing to a
|
||||
// fixed capacity). Since it will significantly increase code complexity,
|
||||
// we prefer to optimize for readability and maintainability at this time.
|
||||
func joinPath(dir, file string) string {
|
||||
buf := make([]byte, 0, len(dir)+len(file)+1)
|
||||
if isAbs(dir) {
|
||||
buf = append(buf, '/')
|
||||
}
|
||||
buf, lookupParent := appendCleanPath(buf, dir, false)
|
||||
buf, _ = appendCleanPath(buf, file, lookupParent)
|
||||
// The appendCleanPath function cleans the path so it does not inject
|
||||
// references to the current directory. If both the dir and file args
|
||||
// were ".", this results in the output buffer being empty so we handle
|
||||
// this condition here.
|
||||
if len(buf) == 0 {
|
||||
buf = append(buf, '.')
|
||||
}
|
||||
// If the file ended with a '/' we make sure that the output also ends
|
||||
// with a '/'. This is needed to ensure that programs have a mechanism
|
||||
// to represent dereferencing symbolic links pointing to directories.
|
||||
if buf[len(buf)-1] != '/' && isDir(file) {
|
||||
buf = append(buf, '/')
|
||||
}
|
||||
return unsafe.String(&buf[0], len(buf))
|
||||
}
|
||||
|
||||
func isAbs(path string) bool {
|
||||
return stringslite.HasPrefix(path, "/")
|
||||
}
|
||||
|
||||
func isDir(path string) bool {
|
||||
return stringslite.HasSuffix(path, "/")
|
||||
}
|
||||
|
||||
// preparePath returns the preopen file descriptor of the directory to perform
|
||||
// path resolution from, along with the pair of pointer and length for the
|
||||
// relative expression of path from the directory.
|
||||
//
|
||||
// If the path argument is not absolute, it is first appended to the current
|
||||
// working directory before resolution.
|
||||
func preparePath(path string) (int32, unsafe.Pointer, size) {
|
||||
var dirFd = int32(-1)
|
||||
var dirName string
|
||||
|
||||
dir := "/"
|
||||
if !isAbs(path) {
|
||||
dir = cwd
|
||||
}
|
||||
path = joinPath(dir, path)
|
||||
|
||||
for _, p := range preopens {
|
||||
if len(p.name) > len(dirName) && stringslite.HasPrefix(path, p.name) {
|
||||
dirFd, dirName = p.fd, p.name
|
||||
}
|
||||
}
|
||||
|
||||
path = path[len(dirName):]
|
||||
for isAbs(path) {
|
||||
path = path[1:]
|
||||
}
|
||||
if len(path) == 0 {
|
||||
path = "."
|
||||
}
|
||||
|
||||
return dirFd, stringPointer(path), size(len(path))
|
||||
}
|
||||
|
||||
func Open(path string, openmode int, perm uint32) (int, error) {
|
||||
if path == "" {
|
||||
return -1, EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
|
||||
var oflags oflags
|
||||
if (openmode & O_CREATE) != 0 {
|
||||
oflags |= OFLAG_CREATE
|
||||
}
|
||||
if (openmode & O_TRUNC) != 0 {
|
||||
oflags |= OFLAG_TRUNC
|
||||
}
|
||||
if (openmode & O_EXCL) != 0 {
|
||||
oflags |= OFLAG_EXCL
|
||||
}
|
||||
|
||||
var rights rights
|
||||
switch openmode & (O_RDONLY | O_WRONLY | O_RDWR) {
|
||||
case O_RDONLY:
|
||||
rights = fileRights & ^writeRights
|
||||
case O_WRONLY:
|
||||
rights = fileRights & ^readRights
|
||||
case O_RDWR:
|
||||
rights = fileRights
|
||||
}
|
||||
|
||||
var fdflags fdflags
|
||||
if (openmode & O_APPEND) != 0 {
|
||||
fdflags |= FDFLAG_APPEND
|
||||
}
|
||||
if (openmode & O_SYNC) != 0 {
|
||||
fdflags |= FDFLAG_SYNC
|
||||
}
|
||||
|
||||
var fd int32
|
||||
errno := path_open(
|
||||
dirFd,
|
||||
LOOKUP_SYMLINK_FOLLOW,
|
||||
pathPtr,
|
||||
pathLen,
|
||||
oflags,
|
||||
rights,
|
||||
fileRights,
|
||||
fdflags,
|
||||
unsafe.Pointer(&fd),
|
||||
)
|
||||
if errno == EISDIR && oflags == 0 && fdflags == 0 && ((rights & writeRights) == 0) {
|
||||
// wasmtime and wasmedge will error if attempting to open a directory
|
||||
// because we are asking for too many rights. However, we cannot
|
||||
// determine ahead of time if the path we are about to open is a
|
||||
// directory, so instead we fallback to a second call to path_open with
|
||||
// a more limited set of rights.
|
||||
//
|
||||
// This approach is subject to a race if the file system is modified
|
||||
// concurrently, so we also inject OFLAG_DIRECTORY to ensure that we do
|
||||
// not accidentally open a file which is not a directory.
|
||||
errno = path_open(
|
||||
dirFd,
|
||||
LOOKUP_SYMLINK_FOLLOW,
|
||||
pathPtr,
|
||||
pathLen,
|
||||
oflags|OFLAG_DIRECTORY,
|
||||
rights&dirRights,
|
||||
fileRights,
|
||||
fdflags,
|
||||
unsafe.Pointer(&fd),
|
||||
)
|
||||
}
|
||||
return int(fd), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Close(fd int) error {
|
||||
errno := fd_close(int32(fd))
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func CloseOnExec(fd int) {
|
||||
// nothing to do - no exec
|
||||
}
|
||||
|
||||
func Mkdir(path string, perm uint32) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
errno := path_create_directory(dirFd, pathPtr, pathLen)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func ReadDir(fd int, buf []byte, cookie dircookie) (int, error) {
|
||||
var nwritten size
|
||||
errno := fd_readdir(int32(fd), unsafe.Pointer(&buf[0]), size(len(buf)), cookie, unsafe.Pointer(&nwritten))
|
||||
return int(nwritten), errnoErr(errno)
|
||||
}
|
||||
|
||||
type Stat_t struct {
|
||||
Dev uint64
|
||||
Ino uint64
|
||||
Filetype uint8
|
||||
Nlink uint64
|
||||
Size uint64
|
||||
Atime uint64
|
||||
Mtime uint64
|
||||
Ctime uint64
|
||||
|
||||
Mode int
|
||||
|
||||
// Uid and Gid are always zero on wasip1 platforms
|
||||
Uid uint32
|
||||
Gid uint32
|
||||
}
|
||||
|
||||
func Stat(path string, st *Stat_t) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
errno := path_filestat_get(dirFd, LOOKUP_SYMLINK_FOLLOW, pathPtr, pathLen, unsafe.Pointer(st))
|
||||
setDefaultMode(st)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Lstat(path string, st *Stat_t) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
errno := path_filestat_get(dirFd, 0, pathPtr, pathLen, unsafe.Pointer(st))
|
||||
setDefaultMode(st)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Fstat(fd int, st *Stat_t) error {
|
||||
errno := fd_filestat_get(int32(fd), unsafe.Pointer(st))
|
||||
setDefaultMode(st)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func setDefaultMode(st *Stat_t) {
|
||||
// WASI does not support unix-like permissions, but Go programs are likely
|
||||
// to expect the permission bits to not be zero so we set defaults to help
|
||||
// avoid breaking applications that are migrating to WASM.
|
||||
if st.Filetype == FILETYPE_DIRECTORY {
|
||||
st.Mode = 0700
|
||||
} else {
|
||||
st.Mode = 0600
|
||||
}
|
||||
}
|
||||
|
||||
func Unlink(path string) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
errno := path_unlink_file(dirFd, pathPtr, pathLen)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Rmdir(path string) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
errno := path_remove_directory(dirFd, pathPtr, pathLen)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Chmod(path string, mode uint32) error {
|
||||
var stat Stat_t
|
||||
return Stat(path, &stat)
|
||||
}
|
||||
|
||||
func Fchmod(fd int, mode uint32) error {
|
||||
var stat Stat_t
|
||||
return Fstat(fd, &stat)
|
||||
}
|
||||
|
||||
func Chown(path string, uid, gid int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func Fchown(fd int, uid, gid int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func Lchown(path string, uid, gid int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func UtimesNano(path string, ts []Timespec) error {
|
||||
// UTIME_OMIT value must match internal/syscall/unix/at_wasip1.go
|
||||
const UTIME_OMIT = -0x2
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
atime := TimespecToNsec(ts[0])
|
||||
mtime := TimespecToNsec(ts[1])
|
||||
if ts[0].Nsec == UTIME_OMIT || ts[1].Nsec == UTIME_OMIT {
|
||||
var st Stat_t
|
||||
if err := Stat(path, &st); err != nil {
|
||||
return err
|
||||
}
|
||||
if ts[0].Nsec == UTIME_OMIT {
|
||||
atime = int64(st.Atime)
|
||||
}
|
||||
if ts[1].Nsec == UTIME_OMIT {
|
||||
mtime = int64(st.Mtime)
|
||||
}
|
||||
}
|
||||
errno := path_filestat_set_times(
|
||||
dirFd,
|
||||
LOOKUP_SYMLINK_FOLLOW,
|
||||
pathPtr,
|
||||
pathLen,
|
||||
timestamp(atime),
|
||||
timestamp(mtime),
|
||||
FILESTAT_SET_ATIM|FILESTAT_SET_MTIM,
|
||||
)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Rename(from, to string) error {
|
||||
if from == "" || to == "" {
|
||||
return EINVAL
|
||||
}
|
||||
oldDirFd, oldPathPtr, oldPathLen := preparePath(from)
|
||||
newDirFd, newPathPtr, newPathLen := preparePath(to)
|
||||
errno := path_rename(
|
||||
oldDirFd,
|
||||
oldPathPtr,
|
||||
oldPathLen,
|
||||
newDirFd,
|
||||
newPathPtr,
|
||||
newPathLen,
|
||||
)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Truncate(path string, length int64) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
fd, err := Open(path, O_WRONLY, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer Close(fd)
|
||||
return Ftruncate(fd, length)
|
||||
}
|
||||
|
||||
func Ftruncate(fd int, length int64) error {
|
||||
errno := fd_filestat_set_size(int32(fd), filesize(length))
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
const ImplementsGetwd = true
|
||||
|
||||
func Getwd() (string, error) {
|
||||
return cwd, nil
|
||||
}
|
||||
|
||||
func Chdir(path string) error {
|
||||
if path == "" {
|
||||
return EINVAL
|
||||
}
|
||||
|
||||
dir := "/"
|
||||
if !isAbs(path) {
|
||||
dir = cwd
|
||||
}
|
||||
path = joinPath(dir, path)
|
||||
|
||||
var stat Stat_t
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
errno := path_filestat_get(dirFd, LOOKUP_SYMLINK_FOLLOW, pathPtr, pathLen, unsafe.Pointer(&stat))
|
||||
if errno != 0 {
|
||||
return errnoErr(errno)
|
||||
}
|
||||
if stat.Filetype != FILETYPE_DIRECTORY {
|
||||
return ENOTDIR
|
||||
}
|
||||
cwd = path
|
||||
return nil
|
||||
}
|
||||
|
||||
func Readlink(path string, buf []byte) (n int, err error) {
|
||||
if path == "" {
|
||||
return 0, EINVAL
|
||||
}
|
||||
if len(buf) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
dirFd, pathPtr, pathLen := preparePath(path)
|
||||
var nwritten size
|
||||
errno := path_readlink(
|
||||
dirFd,
|
||||
pathPtr,
|
||||
pathLen,
|
||||
unsafe.Pointer(&buf[0]),
|
||||
size(len(buf)),
|
||||
unsafe.Pointer(&nwritten),
|
||||
)
|
||||
// For some reason wasmtime returns ERANGE when the output buffer is
|
||||
// shorter than the symbolic link value. os.Readlink expects a nil
|
||||
// error and uses the fact that n is greater or equal to the buffer
|
||||
// length to assume that it needs to try again with a larger size.
|
||||
// This condition is handled in os.Readlink.
|
||||
return int(nwritten), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Link(path, link string) error {
|
||||
if path == "" || link == "" {
|
||||
return EINVAL
|
||||
}
|
||||
oldDirFd, oldPathPtr, oldPathLen := preparePath(path)
|
||||
newDirFd, newPathPtr, newPathLen := preparePath(link)
|
||||
errno := path_link(
|
||||
oldDirFd,
|
||||
0,
|
||||
oldPathPtr,
|
||||
oldPathLen,
|
||||
newDirFd,
|
||||
newPathPtr,
|
||||
newPathLen,
|
||||
)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Symlink(path, link string) error {
|
||||
if path == "" || link == "" {
|
||||
return EINVAL
|
||||
}
|
||||
dirFd, pathPtr, pathlen := preparePath(link)
|
||||
errno := path_symlink(
|
||||
stringPointer(path),
|
||||
size(len(path)),
|
||||
dirFd,
|
||||
pathPtr,
|
||||
pathlen,
|
||||
)
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func Fsync(fd int) error {
|
||||
errno := fd_sync(int32(fd))
|
||||
return errnoErr(errno)
|
||||
}
|
||||
|
||||
func bytesPointer(b []byte) unsafe.Pointer {
|
||||
return unsafe.Pointer(unsafe.SliceData(b))
|
||||
}
|
||||
|
||||
func stringPointer(s string) unsafe.Pointer {
|
||||
return unsafe.Pointer(unsafe.StringData(s))
|
||||
}
|
||||
|
||||
func makeIOVec(b []byte) unsafe.Pointer {
|
||||
return unsafe.Pointer(&iovec{
|
||||
buf: uintptr32(uintptr(bytesPointer(b))),
|
||||
bufLen: size(len(b)),
|
||||
})
|
||||
}
|
||||
|
||||
func Read(fd int, b []byte) (int, error) {
|
||||
var nread size
|
||||
errno := fd_read(int32(fd), makeIOVec(b), 1, unsafe.Pointer(&nread))
|
||||
runtime.KeepAlive(b)
|
||||
return int(nread), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Write(fd int, b []byte) (int, error) {
|
||||
var nwritten size
|
||||
errno := fd_write(int32(fd), makeIOVec(b), 1, unsafe.Pointer(&nwritten))
|
||||
runtime.KeepAlive(b)
|
||||
return int(nwritten), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Pread(fd int, b []byte, offset int64) (int, error) {
|
||||
var nread size
|
||||
errno := fd_pread(int32(fd), makeIOVec(b), 1, filesize(offset), unsafe.Pointer(&nread))
|
||||
runtime.KeepAlive(b)
|
||||
return int(nread), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Pwrite(fd int, b []byte, offset int64) (int, error) {
|
||||
var nwritten size
|
||||
errno := fd_pwrite(int32(fd), makeIOVec(b), 1, filesize(offset), unsafe.Pointer(&nwritten))
|
||||
runtime.KeepAlive(b)
|
||||
return int(nwritten), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Seek(fd int, offset int64, whence int) (int64, error) {
|
||||
var newoffset filesize
|
||||
errno := fd_seek(int32(fd), filedelta(offset), uint32(whence), unsafe.Pointer(&newoffset))
|
||||
return int64(newoffset), errnoErr(errno)
|
||||
}
|
||||
|
||||
func Dup(fd int) (int, error) {
|
||||
return 0, ENOSYS
|
||||
}
|
||||
|
||||
func Dup2(fd, newfd int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func Pipe(fd []int) error {
|
||||
return ENOSYS
|
||||
}
|
||||
|
||||
func RandomGet(b []byte) error {
|
||||
errno := random_get(bytesPointer(b), size(len(b)))
|
||||
return errnoErr(errno)
|
||||
}
|
||||
76
src/syscall/fs_wasip1_test.go
Normal file
76
src/syscall/fs_wasip1_test.go
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2023 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 wasip1
|
||||
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var joinPathTests = [...]struct {
|
||||
dir, file, path string
|
||||
}{
|
||||
0: {".", ".", "."},
|
||||
1: {"./", "./", "./"},
|
||||
2: {"././././", ".", "."},
|
||||
3: {".", "./././", "./"},
|
||||
4: {".", "a", "a"},
|
||||
5: {".", "a/b", "a/b"},
|
||||
6: {".", "..", ".."},
|
||||
7: {".", "../", "../"},
|
||||
8: {".", "../../", "../../"},
|
||||
9: {".", "../..", "../.."},
|
||||
10: {".", "../..//..///", "../../../"},
|
||||
11: {"/", "/", "/"},
|
||||
12: {"/", "a", "/a"},
|
||||
13: {"/", "a/b", "/a/b"},
|
||||
14: {"/a", "b", "/a/b"},
|
||||
15: {"/", ".", "/"},
|
||||
16: {"/", "..", "/"},
|
||||
17: {"/", "../../", "/"},
|
||||
18: {"/", "/../a/b/c", "/a/b/c"},
|
||||
19: {"/", "/../a/b/c", "/a/b/c"},
|
||||
20: {"/", "./hello/world", "/hello/world"},
|
||||
21: {"/a", "../", "/"},
|
||||
22: {"/a/b/c", "..", "/a/b"},
|
||||
23: {"/a/b/c", "..///..///", "/a/"},
|
||||
24: {"/a/b/c", "..///..///..", "/"},
|
||||
25: {"/a/b/c", "..///..///..///..", "/"},
|
||||
26: {"/a/b/c", "..///..///..///..///..", "/"},
|
||||
27: {"/a/b/c/", "/d/e/f/", "/a/b/c/d/e/f/"},
|
||||
28: {"a/b/c/", ".", "a/b/c"},
|
||||
29: {"a/b/c/", "./d", "a/b/c/d"},
|
||||
30: {"a/b/c/", "./d/", "a/b/c/d/"},
|
||||
31: {"a/b/", "./c/d/", "a/b/c/d/"},
|
||||
32: {"../", "..", "../.."},
|
||||
33: {"a/b/c/d", "e/../..", "a/b/c"},
|
||||
34: {"a/b/c/d", "./e/../..", "a/b/c"},
|
||||
35: {"a/b/c/d", "./e/..//../../f/g//", "a/b/f/g/"},
|
||||
36: {"../../../", "a/../../b/c", "../../b/c"},
|
||||
37: {"/a/b/c", "/.././/hey!", "/a/b/hey!"},
|
||||
}
|
||||
|
||||
func TestJoinPath(t *testing.T) {
|
||||
for _, test := range joinPathTests {
|
||||
t.Run("", func(t *testing.T) {
|
||||
path := syscall.JoinPath(test.dir, test.file)
|
||||
if path != test.path {
|
||||
t.Errorf("join(%q,%q): want=%q got=%q", test.dir, test.file, test.path, path)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkJoinPath(b *testing.B) {
|
||||
for _, test := range joinPathTests {
|
||||
b.Run("", func(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
syscall.JoinPath(test.dir, test.file)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
84
src/syscall/getdirentries_test.go
Normal file
84
src/syscall/getdirentries_test.go
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2019 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 || freebsd || netbsd || openbsd
|
||||
|
||||
package syscall_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func TestGetdirentries(t *testing.T) {
|
||||
for _, count := range []int{10, 1000} {
|
||||
t.Run(fmt.Sprintf("n=%d", count), func(t *testing.T) {
|
||||
testGetdirentries(t, count)
|
||||
})
|
||||
}
|
||||
}
|
||||
func testGetdirentries(t *testing.T, count int) {
|
||||
if count > 100 && testing.Short() && os.Getenv("GO_BUILDER_NAME") == "" {
|
||||
t.Skip("skipping in -short mode")
|
||||
}
|
||||
d := t.TempDir()
|
||||
var names []string
|
||||
for i := 0; i < count; i++ {
|
||||
names = append(names, fmt.Sprintf("file%03d", i))
|
||||
}
|
||||
|
||||
// Make files in the temp directory
|
||||
for _, name := range names {
|
||||
err := os.WriteFile(filepath.Join(d, name), []byte("data"), 0)
|
||||
if err != nil {
|
||||
t.Fatalf("WriteFile: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Read files using Getdirentries
|
||||
var names2 []string
|
||||
fd, err := syscall.Open(d, syscall.O_RDONLY, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("Open: %v", err)
|
||||
}
|
||||
defer syscall.Close(fd)
|
||||
var base uintptr
|
||||
var buf [2048]byte
|
||||
for {
|
||||
n, err := syscall.Getdirentries(fd, buf[:], &base)
|
||||
if err != nil {
|
||||
t.Fatalf("Getdirentries: %v", err)
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
data := buf[:n]
|
||||
for len(data) > 0 {
|
||||
// If multiple Dirents are written into buf, sometimes when we reach the final one,
|
||||
// we have cap(buf) < Sizeof(Dirent). So use an appropriate slice to copy from data.
|
||||
var dirent syscall.Dirent
|
||||
copy((*[unsafe.Sizeof(dirent)]byte)(unsafe.Pointer(&dirent))[:], data)
|
||||
|
||||
data = data[dirent.Reclen:]
|
||||
name := make([]byte, dirent.Namlen)
|
||||
for i := 0; i < int(dirent.Namlen); i++ {
|
||||
name[i] = byte(dirent.Name[i])
|
||||
}
|
||||
names2 = append(names2, string(name))
|
||||
}
|
||||
}
|
||||
|
||||
names = append(names, ".", "..") // Getdirentries returns these also
|
||||
slices.Sort(names)
|
||||
slices.Sort(names2)
|
||||
if strings.Join(names, ":") != strings.Join(names2, ":") {
|
||||
t.Errorf("names don't match\n names: %q\nnames2: %q", names, names2)
|
||||
}
|
||||
}
|
||||
9
src/syscall/js/export_test.go
Normal file
9
src/syscall/js/export_test.go
Normal file
@@ -0,0 +1,9 @@
|
||||
// Copyright 2018 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 js && wasm
|
||||
|
||||
package js
|
||||
|
||||
var JSGo = jsGo
|
||||
105
src/syscall/js/func.go
Normal file
105
src/syscall/js/func.go
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright 2018 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 js && wasm
|
||||
|
||||
package js
|
||||
|
||||
import "sync"
|
||||
|
||||
var (
|
||||
funcsMu sync.Mutex
|
||||
funcs = make(map[uint32]func(Value, []Value) any)
|
||||
nextFuncID uint32 = 1
|
||||
)
|
||||
|
||||
// Func is a wrapped Go function to be called by JavaScript.
|
||||
type Func struct {
|
||||
Value // the JavaScript function that invokes the Go function
|
||||
id uint32
|
||||
}
|
||||
|
||||
// FuncOf returns a function to be used by JavaScript.
|
||||
//
|
||||
// The Go function fn is called with the value of JavaScript's "this" keyword and the
|
||||
// arguments of the invocation. The return value of the invocation is
|
||||
// the result of the Go function mapped back to JavaScript according to ValueOf.
|
||||
//
|
||||
// Invoking the wrapped Go function from JavaScript will
|
||||
// pause the event loop and spawn a new goroutine.
|
||||
// Other wrapped functions which are triggered during a call from Go to JavaScript
|
||||
// get executed on the same goroutine.
|
||||
//
|
||||
// As a consequence, if one wrapped function blocks, JavaScript's event loop
|
||||
// is blocked until that function returns. Hence, calling any async JavaScript
|
||||
// API, which requires the event loop, like fetch (http.Client), will cause an
|
||||
// immediate deadlock. Therefore a blocking function should explicitly start a
|
||||
// new goroutine.
|
||||
//
|
||||
// Func.Release must be called to free up resources when the function will not be invoked any more.
|
||||
func FuncOf(fn func(this Value, args []Value) any) Func {
|
||||
funcsMu.Lock()
|
||||
id := nextFuncID
|
||||
nextFuncID++
|
||||
funcs[id] = fn
|
||||
funcsMu.Unlock()
|
||||
return Func{
|
||||
id: id,
|
||||
Value: jsGo.Call("_makeFuncWrapper", id),
|
||||
}
|
||||
}
|
||||
|
||||
// Release frees up resources allocated for the function.
|
||||
// The function must not be invoked after calling Release.
|
||||
// It is allowed to call Release while the function is still running.
|
||||
func (c Func) Release() {
|
||||
funcsMu.Lock()
|
||||
delete(funcs, c.id)
|
||||
funcsMu.Unlock()
|
||||
}
|
||||
|
||||
// setEventHandler is defined in the runtime package.
|
||||
func setEventHandler(fn func() bool)
|
||||
|
||||
func init() {
|
||||
setEventHandler(handleEvent)
|
||||
}
|
||||
|
||||
// handleEvent retrieves the pending event (window._pendingEvent) and calls the js.Func on it.
|
||||
// It returns true if an event was handled.
|
||||
func handleEvent() bool {
|
||||
// Retrieve the event from js
|
||||
cb := jsGo.Get("_pendingEvent")
|
||||
if cb.IsNull() {
|
||||
return false
|
||||
}
|
||||
jsGo.Set("_pendingEvent", Null())
|
||||
|
||||
id := uint32(cb.Get("id").Int())
|
||||
if id == 0 { // zero indicates deadlock
|
||||
select {}
|
||||
}
|
||||
|
||||
// Retrieve the associated js.Func
|
||||
funcsMu.Lock()
|
||||
f, ok := funcs[id]
|
||||
funcsMu.Unlock()
|
||||
if !ok {
|
||||
Global().Get("console").Call("error", "call to released function")
|
||||
return true
|
||||
}
|
||||
|
||||
// Call the js.Func with arguments
|
||||
this := cb.Get("this")
|
||||
argsObj := cb.Get("args")
|
||||
args := make([]Value, argsObj.Length())
|
||||
for i := range args {
|
||||
args[i] = argsObj.Index(i)
|
||||
}
|
||||
result := f(this, args)
|
||||
|
||||
// Return the result to js
|
||||
cb.Set("result", result)
|
||||
return true
|
||||
}
|
||||
687
src/syscall/js/js.go
Normal file
687
src/syscall/js/js.go
Normal file
@@ -0,0 +1,687 @@
|
||||
// Copyright 2018 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 js && wasm
|
||||
|
||||
// Package js gives access to the WebAssembly host environment when using the js/wasm architecture.
|
||||
// Its API is based on JavaScript semantics.
|
||||
//
|
||||
// This package is EXPERIMENTAL. Its current scope is only to allow tests to run, but not yet to provide a
|
||||
// comprehensive API for users. It is exempt from the Go compatibility promise.
|
||||
package js
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// ref is used to identify a JavaScript value, since the value itself can not be passed to WebAssembly.
|
||||
//
|
||||
// The JavaScript value "undefined" is represented by the value 0.
|
||||
// A JavaScript number (64-bit float, except 0 and NaN) is represented by its IEEE 754 binary representation.
|
||||
// All other values are represented as an IEEE 754 binary representation of NaN with bits 0-31 used as
|
||||
// an ID and bits 32-34 used to differentiate between string, symbol, function and object.
|
||||
type ref uint64
|
||||
|
||||
// nanHead are the upper 32 bits of a ref which are set if the value is not encoded as an IEEE 754 number (see above).
|
||||
const nanHead = 0x7FF80000
|
||||
|
||||
// Value represents a JavaScript value. The zero value is the JavaScript value "undefined".
|
||||
// Values can be checked for equality with the Equal method.
|
||||
type Value struct {
|
||||
_ [0]func() // uncomparable; to make == not compile
|
||||
ref ref // identifies a JavaScript value, see ref type
|
||||
gcPtr *ref // used to trigger the finalizer when the Value is not referenced any more
|
||||
}
|
||||
|
||||
const (
|
||||
// the type flags need to be in sync with wasm_exec.js
|
||||
typeFlagNone = iota
|
||||
typeFlagObject
|
||||
typeFlagString
|
||||
typeFlagSymbol
|
||||
typeFlagFunction
|
||||
)
|
||||
|
||||
func makeValue(r ref) Value {
|
||||
var gcPtr *ref
|
||||
typeFlag := (r >> 32) & 7
|
||||
if (r>>32)&nanHead == nanHead && typeFlag != typeFlagNone {
|
||||
gcPtr = new(ref)
|
||||
*gcPtr = r
|
||||
runtime.SetFinalizer(gcPtr, func(p *ref) {
|
||||
finalizeRef(*p)
|
||||
})
|
||||
}
|
||||
|
||||
return Value{ref: r, gcPtr: gcPtr}
|
||||
}
|
||||
|
||||
//go:wasmimport gojs syscall/js.finalizeRef
|
||||
func finalizeRef(r ref)
|
||||
|
||||
func predefValue(id uint32, typeFlag byte) Value {
|
||||
return Value{ref: (nanHead|ref(typeFlag))<<32 | ref(id)}
|
||||
}
|
||||
|
||||
func floatValue(f float64) Value {
|
||||
if f == 0 {
|
||||
return valueZero
|
||||
}
|
||||
if f != f {
|
||||
return valueNaN
|
||||
}
|
||||
return Value{ref: *(*ref)(unsafe.Pointer(&f))}
|
||||
}
|
||||
|
||||
// Error wraps a JavaScript error.
|
||||
type Error struct {
|
||||
// Value is the underlying JavaScript error value.
|
||||
Value
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e Error) Error() string {
|
||||
return "JavaScript error: " + e.Get("message").String()
|
||||
}
|
||||
|
||||
var (
|
||||
valueUndefined = Value{ref: 0}
|
||||
valueNaN = predefValue(0, typeFlagNone)
|
||||
valueZero = predefValue(1, typeFlagNone)
|
||||
valueNull = predefValue(2, typeFlagNone)
|
||||
valueTrue = predefValue(3, typeFlagNone)
|
||||
valueFalse = predefValue(4, typeFlagNone)
|
||||
valueGlobal = predefValue(5, typeFlagObject)
|
||||
jsGo = predefValue(6, typeFlagObject) // instance of the Go class in JavaScript
|
||||
|
||||
objectConstructor = valueGlobal.Get("Object")
|
||||
arrayConstructor = valueGlobal.Get("Array")
|
||||
)
|
||||
|
||||
// Equal reports whether v and w are equal according to JavaScript's === operator.
|
||||
func (v Value) Equal(w Value) bool {
|
||||
return v.ref == w.ref && v.ref != valueNaN.ref
|
||||
}
|
||||
|
||||
// Undefined returns the JavaScript value "undefined".
|
||||
func Undefined() Value {
|
||||
return valueUndefined
|
||||
}
|
||||
|
||||
// IsUndefined reports whether v is the JavaScript value "undefined".
|
||||
func (v Value) IsUndefined() bool {
|
||||
return v.ref == valueUndefined.ref
|
||||
}
|
||||
|
||||
// Null returns the JavaScript value "null".
|
||||
func Null() Value {
|
||||
return valueNull
|
||||
}
|
||||
|
||||
// IsNull reports whether v is the JavaScript value "null".
|
||||
func (v Value) IsNull() bool {
|
||||
return v.ref == valueNull.ref
|
||||
}
|
||||
|
||||
// IsNaN reports whether v is the JavaScript value "NaN".
|
||||
func (v Value) IsNaN() bool {
|
||||
return v.ref == valueNaN.ref
|
||||
}
|
||||
|
||||
// Global returns the JavaScript global object, usually "window" or "global".
|
||||
func Global() Value {
|
||||
return valueGlobal
|
||||
}
|
||||
|
||||
// ValueOf returns x as a JavaScript value:
|
||||
//
|
||||
// | Go | JavaScript |
|
||||
// | ---------------------- | ---------------------- |
|
||||
// | js.Value | [its value] |
|
||||
// | js.Func | function |
|
||||
// | nil | null |
|
||||
// | bool | boolean |
|
||||
// | integers and floats | number |
|
||||
// | string | string |
|
||||
// | []interface{} | new array |
|
||||
// | map[string]interface{} | new object |
|
||||
//
|
||||
// Panics if x is not one of the expected types.
|
||||
func ValueOf(x any) Value {
|
||||
switch x := x.(type) {
|
||||
case Value:
|
||||
return x
|
||||
case Func:
|
||||
return x.Value
|
||||
case nil:
|
||||
return valueNull
|
||||
case bool:
|
||||
if x {
|
||||
return valueTrue
|
||||
} else {
|
||||
return valueFalse
|
||||
}
|
||||
case int:
|
||||
return floatValue(float64(x))
|
||||
case int8:
|
||||
return floatValue(float64(x))
|
||||
case int16:
|
||||
return floatValue(float64(x))
|
||||
case int32:
|
||||
return floatValue(float64(x))
|
||||
case int64:
|
||||
return floatValue(float64(x))
|
||||
case uint:
|
||||
return floatValue(float64(x))
|
||||
case uint8:
|
||||
return floatValue(float64(x))
|
||||
case uint16:
|
||||
return floatValue(float64(x))
|
||||
case uint32:
|
||||
return floatValue(float64(x))
|
||||
case uint64:
|
||||
return floatValue(float64(x))
|
||||
case uintptr:
|
||||
return floatValue(float64(x))
|
||||
case unsafe.Pointer:
|
||||
return floatValue(float64(uintptr(x)))
|
||||
case float32:
|
||||
return floatValue(float64(x))
|
||||
case float64:
|
||||
return floatValue(x)
|
||||
case string:
|
||||
return makeValue(stringVal(x))
|
||||
case []any:
|
||||
a := arrayConstructor.New(len(x))
|
||||
for i, s := range x {
|
||||
a.SetIndex(i, s)
|
||||
}
|
||||
return a
|
||||
case map[string]any:
|
||||
o := objectConstructor.New()
|
||||
for k, v := range x {
|
||||
o.Set(k, v)
|
||||
}
|
||||
return o
|
||||
default:
|
||||
panic("ValueOf: invalid value")
|
||||
}
|
||||
}
|
||||
|
||||
// stringVal copies string x to Javascript and returns a ref.
|
||||
//
|
||||
// (noescape): This is safe because no references are maintained to the
|
||||
// Go string x after the syscall returns.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.stringVal
|
||||
//go:noescape
|
||||
func stringVal(x string) ref
|
||||
|
||||
// Type represents the JavaScript type of a Value.
|
||||
type Type int
|
||||
|
||||
const (
|
||||
TypeUndefined Type = iota
|
||||
TypeNull
|
||||
TypeBoolean
|
||||
TypeNumber
|
||||
TypeString
|
||||
TypeSymbol
|
||||
TypeObject
|
||||
TypeFunction
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeUndefined:
|
||||
return "undefined"
|
||||
case TypeNull:
|
||||
return "null"
|
||||
case TypeBoolean:
|
||||
return "boolean"
|
||||
case TypeNumber:
|
||||
return "number"
|
||||
case TypeString:
|
||||
return "string"
|
||||
case TypeSymbol:
|
||||
return "symbol"
|
||||
case TypeObject:
|
||||
return "object"
|
||||
case TypeFunction:
|
||||
return "function"
|
||||
default:
|
||||
panic("bad type")
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) isObject() bool {
|
||||
return t == TypeObject || t == TypeFunction
|
||||
}
|
||||
|
||||
// Type returns the JavaScript type of the value v. It is similar to JavaScript's typeof operator,
|
||||
// except that it returns TypeNull instead of TypeObject for null.
|
||||
func (v Value) Type() Type {
|
||||
switch v.ref {
|
||||
case valueUndefined.ref:
|
||||
return TypeUndefined
|
||||
case valueNull.ref:
|
||||
return TypeNull
|
||||
case valueTrue.ref, valueFalse.ref:
|
||||
return TypeBoolean
|
||||
}
|
||||
if v.isNumber() {
|
||||
return TypeNumber
|
||||
}
|
||||
typeFlag := (v.ref >> 32) & 7
|
||||
switch typeFlag {
|
||||
case typeFlagObject:
|
||||
return TypeObject
|
||||
case typeFlagString:
|
||||
return TypeString
|
||||
case typeFlagSymbol:
|
||||
return TypeSymbol
|
||||
case typeFlagFunction:
|
||||
return TypeFunction
|
||||
default:
|
||||
panic("bad type flag")
|
||||
}
|
||||
}
|
||||
|
||||
// Get returns the JavaScript property p of value v.
|
||||
// It panics if v is not a JavaScript object.
|
||||
func (v Value) Get(p string) Value {
|
||||
if vType := v.Type(); !vType.isObject() {
|
||||
panic(&ValueError{"Value.Get", vType})
|
||||
}
|
||||
r := makeValue(valueGet(v.ref, p))
|
||||
runtime.KeepAlive(v)
|
||||
return r
|
||||
}
|
||||
|
||||
// valueGet returns a ref to JavaScript property p of ref v.
|
||||
//
|
||||
// (noescape): This is safe because no references are maintained to the
|
||||
// Go string p after the syscall returns.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueGet
|
||||
//go:noescape
|
||||
func valueGet(v ref, p string) ref
|
||||
|
||||
// Set sets the JavaScript property p of value v to ValueOf(x).
|
||||
// It panics if v is not a JavaScript object.
|
||||
func (v Value) Set(p string, x any) {
|
||||
if vType := v.Type(); !vType.isObject() {
|
||||
panic(&ValueError{"Value.Set", vType})
|
||||
}
|
||||
xv := ValueOf(x)
|
||||
valueSet(v.ref, p, xv.ref)
|
||||
runtime.KeepAlive(v)
|
||||
runtime.KeepAlive(xv)
|
||||
}
|
||||
|
||||
// valueSet sets property p of ref v to ref x.
|
||||
//
|
||||
// (noescape): This is safe because no references are maintained to the
|
||||
// Go string p after the syscall returns.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueSet
|
||||
//go:noescape
|
||||
func valueSet(v ref, p string, x ref)
|
||||
|
||||
// Delete deletes the JavaScript property p of value v.
|
||||
// It panics if v is not a JavaScript object.
|
||||
func (v Value) Delete(p string) {
|
||||
if vType := v.Type(); !vType.isObject() {
|
||||
panic(&ValueError{"Value.Delete", vType})
|
||||
}
|
||||
valueDelete(v.ref, p)
|
||||
runtime.KeepAlive(v)
|
||||
}
|
||||
|
||||
// valueDelete deletes the JavaScript property p of ref v.
|
||||
//
|
||||
// (noescape): This is safe because no references are maintained to the
|
||||
// Go string p after the syscall returns.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueDelete
|
||||
//go:noescape
|
||||
func valueDelete(v ref, p string)
|
||||
|
||||
// Index returns JavaScript index i of value v.
|
||||
// It panics if v is not a JavaScript object.
|
||||
func (v Value) Index(i int) Value {
|
||||
if vType := v.Type(); !vType.isObject() {
|
||||
panic(&ValueError{"Value.Index", vType})
|
||||
}
|
||||
r := makeValue(valueIndex(v.ref, i))
|
||||
runtime.KeepAlive(v)
|
||||
return r
|
||||
}
|
||||
|
||||
//go:wasmimport gojs syscall/js.valueIndex
|
||||
func valueIndex(v ref, i int) ref
|
||||
|
||||
// SetIndex sets the JavaScript index i of value v to ValueOf(x).
|
||||
// It panics if v is not a JavaScript object.
|
||||
func (v Value) SetIndex(i int, x any) {
|
||||
if vType := v.Type(); !vType.isObject() {
|
||||
panic(&ValueError{"Value.SetIndex", vType})
|
||||
}
|
||||
xv := ValueOf(x)
|
||||
valueSetIndex(v.ref, i, xv.ref)
|
||||
runtime.KeepAlive(v)
|
||||
runtime.KeepAlive(xv)
|
||||
}
|
||||
|
||||
//go:wasmimport gojs syscall/js.valueSetIndex
|
||||
func valueSetIndex(v ref, i int, x ref)
|
||||
|
||||
// makeArgSlices makes two slices to hold JavaScript arg data.
|
||||
// It can be paired with storeArgs to make-and-store JavaScript arg slices.
|
||||
// However, the two functions are separated to ensure makeArgSlices is inlined
|
||||
// which will prevent the slices from being heap allocated for small (<=16)
|
||||
// numbers of args.
|
||||
func makeArgSlices(size int) (argVals []Value, argRefs []ref) {
|
||||
// value chosen for being power of two, and enough to handle all web APIs
|
||||
// in particular, note that WebGL2's texImage2D takes up to 10 arguments
|
||||
const maxStackArgs = 16
|
||||
if size <= maxStackArgs {
|
||||
// as long as makeArgs is inlined, these will be stack-allocated
|
||||
argVals = make([]Value, size, maxStackArgs)
|
||||
argRefs = make([]ref, size, maxStackArgs)
|
||||
} else {
|
||||
// allocates on the heap, but exceeding maxStackArgs should be rare
|
||||
argVals = make([]Value, size)
|
||||
argRefs = make([]ref, size)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// storeArgs maps input args onto respective Value and ref slices.
|
||||
// It can be paired with makeArgSlices to make-and-store JavaScript arg slices.
|
||||
func storeArgs(args []any, argValsDst []Value, argRefsDst []ref) {
|
||||
// would go in makeArgs if the combined func was simple enough to inline
|
||||
for i, arg := range args {
|
||||
v := ValueOf(arg)
|
||||
argValsDst[i] = v
|
||||
argRefsDst[i] = v.ref
|
||||
}
|
||||
}
|
||||
|
||||
// Length returns the JavaScript property "length" of v.
|
||||
// It panics if v is not a JavaScript object.
|
||||
func (v Value) Length() int {
|
||||
if vType := v.Type(); !vType.isObject() {
|
||||
panic(&ValueError{"Value.SetIndex", vType})
|
||||
}
|
||||
r := valueLength(v.ref)
|
||||
runtime.KeepAlive(v)
|
||||
return r
|
||||
}
|
||||
|
||||
//go:wasmimport gojs syscall/js.valueLength
|
||||
func valueLength(v ref) int
|
||||
|
||||
// Call does a JavaScript call to the method m of value v with the given arguments.
|
||||
// It panics if v has no method m.
|
||||
// The arguments get mapped to JavaScript values according to the ValueOf function.
|
||||
func (v Value) Call(m string, args ...any) Value {
|
||||
argVals, argRefs := makeArgSlices(len(args))
|
||||
storeArgs(args, argVals, argRefs)
|
||||
res, ok := valueCall(v.ref, m, argRefs)
|
||||
runtime.KeepAlive(v)
|
||||
runtime.KeepAlive(argVals)
|
||||
if !ok {
|
||||
if vType := v.Type(); !vType.isObject() { // check here to avoid overhead in success case
|
||||
panic(&ValueError{"Value.Call", vType})
|
||||
}
|
||||
if propType := v.Get(m).Type(); propType != TypeFunction {
|
||||
panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String())
|
||||
}
|
||||
panic(Error{makeValue(res)})
|
||||
}
|
||||
return makeValue(res)
|
||||
}
|
||||
|
||||
// valueCall does a JavaScript call to the method name m of ref v with the given arguments.
|
||||
//
|
||||
// (noescape): This is safe because no references are maintained to the
|
||||
// Go string m after the syscall returns. Additionally, the args slice
|
||||
// is only used temporarily to collect the JavaScript objects for
|
||||
// the JavaScript method invocation.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueCall
|
||||
//go:nosplit
|
||||
//go:noescape
|
||||
func valueCall(v ref, m string, args []ref) (ref, bool)
|
||||
|
||||
// Invoke does a JavaScript call of the value v with the given arguments.
|
||||
// It panics if v is not a JavaScript function.
|
||||
// The arguments get mapped to JavaScript values according to the ValueOf function.
|
||||
func (v Value) Invoke(args ...any) Value {
|
||||
argVals, argRefs := makeArgSlices(len(args))
|
||||
storeArgs(args, argVals, argRefs)
|
||||
res, ok := valueInvoke(v.ref, argRefs)
|
||||
runtime.KeepAlive(v)
|
||||
runtime.KeepAlive(argVals)
|
||||
if !ok {
|
||||
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
|
||||
panic(&ValueError{"Value.Invoke", vType})
|
||||
}
|
||||
panic(Error{makeValue(res)})
|
||||
}
|
||||
return makeValue(res)
|
||||
}
|
||||
|
||||
// valueInvoke does a JavaScript call to value v with the given arguments.
|
||||
//
|
||||
// (noescape): This is safe because the args slice is only used temporarily
|
||||
// to collect the JavaScript objects for the JavaScript method
|
||||
// invocation.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueInvoke
|
||||
//go:noescape
|
||||
func valueInvoke(v ref, args []ref) (ref, bool)
|
||||
|
||||
// New uses JavaScript's "new" operator with value v as constructor and the given arguments.
|
||||
// It panics if v is not a JavaScript function.
|
||||
// The arguments get mapped to JavaScript values according to the ValueOf function.
|
||||
func (v Value) New(args ...any) Value {
|
||||
argVals, argRefs := makeArgSlices(len(args))
|
||||
storeArgs(args, argVals, argRefs)
|
||||
res, ok := valueNew(v.ref, argRefs)
|
||||
runtime.KeepAlive(v)
|
||||
runtime.KeepAlive(argVals)
|
||||
if !ok {
|
||||
if vType := v.Type(); vType != TypeFunction { // check here to avoid overhead in success case
|
||||
panic(&ValueError{"Value.Invoke", vType})
|
||||
}
|
||||
panic(Error{makeValue(res)})
|
||||
}
|
||||
return makeValue(res)
|
||||
}
|
||||
|
||||
// valueNew uses JavaScript's "new" operator with value v as a constructor and the given arguments.
|
||||
//
|
||||
// (noescape): This is safe because the args slice is only used temporarily
|
||||
// to collect the JavaScript objects for the constructor execution.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueNew
|
||||
//go:noescape
|
||||
func valueNew(v ref, args []ref) (ref, bool)
|
||||
|
||||
func (v Value) isNumber() bool {
|
||||
return v.ref == valueZero.ref ||
|
||||
v.ref == valueNaN.ref ||
|
||||
(v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead)
|
||||
}
|
||||
|
||||
func (v Value) float(method string) float64 {
|
||||
if !v.isNumber() {
|
||||
panic(&ValueError{method, v.Type()})
|
||||
}
|
||||
if v.ref == valueZero.ref {
|
||||
return 0
|
||||
}
|
||||
return *(*float64)(unsafe.Pointer(&v.ref))
|
||||
}
|
||||
|
||||
// Float returns the value v as a float64.
|
||||
// It panics if v is not a JavaScript number.
|
||||
func (v Value) Float() float64 {
|
||||
return v.float("Value.Float")
|
||||
}
|
||||
|
||||
// Int returns the value v truncated to an int.
|
||||
// It panics if v is not a JavaScript number.
|
||||
func (v Value) Int() int {
|
||||
return int(v.float("Value.Int"))
|
||||
}
|
||||
|
||||
// Bool returns the value v as a bool.
|
||||
// It panics if v is not a JavaScript boolean.
|
||||
func (v Value) Bool() bool {
|
||||
switch v.ref {
|
||||
case valueTrue.ref:
|
||||
return true
|
||||
case valueFalse.ref:
|
||||
return false
|
||||
default:
|
||||
panic(&ValueError{"Value.Bool", v.Type()})
|
||||
}
|
||||
}
|
||||
|
||||
// Truthy returns the JavaScript "truthiness" of the value v. In JavaScript,
|
||||
// false, 0, "", null, undefined, and NaN are "falsy", and everything else is
|
||||
// "truthy". See https://developer.mozilla.org/en-US/docs/Glossary/Truthy.
|
||||
func (v Value) Truthy() bool {
|
||||
switch v.Type() {
|
||||
case TypeUndefined, TypeNull:
|
||||
return false
|
||||
case TypeBoolean:
|
||||
return v.Bool()
|
||||
case TypeNumber:
|
||||
return v.ref != valueNaN.ref && v.ref != valueZero.ref
|
||||
case TypeString:
|
||||
return v.String() != ""
|
||||
case TypeSymbol, TypeFunction, TypeObject:
|
||||
return true
|
||||
default:
|
||||
panic("bad type")
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the value v as a string.
|
||||
// String is a special case because of Go's String method convention. Unlike the other getters,
|
||||
// it does not panic if v's Type is not TypeString. Instead, it returns a string of the form "<T>"
|
||||
// or "<T: V>" where T is v's type and V is a string representation of v's value.
|
||||
func (v Value) String() string {
|
||||
switch v.Type() {
|
||||
case TypeString:
|
||||
return jsString(v)
|
||||
case TypeUndefined:
|
||||
return "<undefined>"
|
||||
case TypeNull:
|
||||
return "<null>"
|
||||
case TypeBoolean:
|
||||
return "<boolean: " + jsString(v) + ">"
|
||||
case TypeNumber:
|
||||
return "<number: " + jsString(v) + ">"
|
||||
case TypeSymbol:
|
||||
return "<symbol>"
|
||||
case TypeObject:
|
||||
return "<object>"
|
||||
case TypeFunction:
|
||||
return "<function>"
|
||||
default:
|
||||
panic("bad type")
|
||||
}
|
||||
}
|
||||
|
||||
func jsString(v Value) string {
|
||||
str, length := valuePrepareString(v.ref)
|
||||
runtime.KeepAlive(v)
|
||||
b := make([]byte, length)
|
||||
valueLoadString(str, b)
|
||||
finalizeRef(str)
|
||||
return string(b)
|
||||
}
|
||||
|
||||
//go:wasmimport gojs syscall/js.valuePrepareString
|
||||
func valuePrepareString(v ref) (ref, int)
|
||||
|
||||
// valueLoadString loads string data located at ref v into byte slice b.
|
||||
//
|
||||
// (noescape): This is safe because the byte slice is only used as a destination
|
||||
// for storing the string data and references to it are not maintained.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.valueLoadString
|
||||
//go:noescape
|
||||
func valueLoadString(v ref, b []byte)
|
||||
|
||||
// InstanceOf reports whether v is an instance of type t according to JavaScript's instanceof operator.
|
||||
func (v Value) InstanceOf(t Value) bool {
|
||||
r := valueInstanceOf(v.ref, t.ref)
|
||||
runtime.KeepAlive(v)
|
||||
runtime.KeepAlive(t)
|
||||
return r
|
||||
}
|
||||
|
||||
//go:wasmimport gojs syscall/js.valueInstanceOf
|
||||
func valueInstanceOf(v ref, t ref) bool
|
||||
|
||||
// A ValueError occurs when a Value method is invoked on
|
||||
// a Value that does not support it. Such cases are documented
|
||||
// in the description of each method.
|
||||
type ValueError struct {
|
||||
Method string
|
||||
Type Type
|
||||
}
|
||||
|
||||
func (e *ValueError) Error() string {
|
||||
return "syscall/js: call of " + e.Method + " on " + e.Type.String()
|
||||
}
|
||||
|
||||
// CopyBytesToGo copies bytes from src to dst.
|
||||
// It panics if src is not a Uint8Array or Uint8ClampedArray.
|
||||
// It returns the number of bytes copied, which will be the minimum of the lengths of src and dst.
|
||||
func CopyBytesToGo(dst []byte, src Value) int {
|
||||
n, ok := copyBytesToGo(dst, src.ref)
|
||||
runtime.KeepAlive(src)
|
||||
if !ok {
|
||||
panic("syscall/js: CopyBytesToGo: expected src to be a Uint8Array or Uint8ClampedArray")
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// copyBytesToGo copies bytes from src to dst.
|
||||
//
|
||||
// (noescape): This is safe because the dst byte slice is only used as a dst
|
||||
// copy buffer and no references to it are maintained.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.copyBytesToGo
|
||||
//go:noescape
|
||||
func copyBytesToGo(dst []byte, src ref) (int, bool)
|
||||
|
||||
// CopyBytesToJS copies bytes from src to dst.
|
||||
// It panics if dst is not a Uint8Array or Uint8ClampedArray.
|
||||
// It returns the number of bytes copied, which will be the minimum of the lengths of src and dst.
|
||||
func CopyBytesToJS(dst Value, src []byte) int {
|
||||
n, ok := copyBytesToJS(dst.ref, src)
|
||||
runtime.KeepAlive(dst)
|
||||
if !ok {
|
||||
panic("syscall/js: CopyBytesToJS: expected dst to be a Uint8Array or Uint8ClampedArray")
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// copyBytesToJs copies bytes from src to dst.
|
||||
//
|
||||
// (noescape): This is safe because the src byte slice is only used as a src
|
||||
// copy buffer and no references to it are maintained.
|
||||
//
|
||||
//go:wasmimport gojs syscall/js.copyBytesToJS
|
||||
//go:noescape
|
||||
func copyBytesToJS(dst ref, src []byte) (int, bool)
|
||||
7
src/syscall/js/js_js.s
Normal file
7
src/syscall/js/js_js.s
Normal file
@@ -0,0 +1,7 @@
|
||||
// Copyright 2018 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.
|
||||
|
||||
// The runtime package uses //go:linkname to push the setEventHandler to this
|
||||
// package. To prevent the go tool from passing -complete to the compile tool,
|
||||
// this file must remain stubbed out.
|
||||
690
src/syscall/js/js_test.go
Normal file
690
src/syscall/js/js_test.go
Normal file
@@ -0,0 +1,690 @@
|
||||
// Copyright 2018 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 js && wasm
|
||||
|
||||
// To run these tests:
|
||||
//
|
||||
// - Install Node
|
||||
// - Add /path/to/go/misc/wasm to your $PATH (so that "go test" can find
|
||||
// "go_js_wasm_exec").
|
||||
// - GOOS=js GOARCH=wasm go test
|
||||
//
|
||||
// See -exec in "go help test", and "go help run" for details.
|
||||
|
||||
package js_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"syscall/js"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var dummys = js.Global().Call("eval", `({
|
||||
someBool: true,
|
||||
someString: "abc\u1234",
|
||||
someInt: 42,
|
||||
someFloat: 42.123,
|
||||
someArray: [41, 42, 43],
|
||||
someDate: new Date(),
|
||||
add: function(a, b) {
|
||||
return a + b;
|
||||
},
|
||||
zero: 0,
|
||||
stringZero: "0",
|
||||
NaN: NaN,
|
||||
emptyObj: {},
|
||||
emptyArray: [],
|
||||
Infinity: Infinity,
|
||||
NegInfinity: -Infinity,
|
||||
objNumber0: new Number(0),
|
||||
objBooleanFalse: new Boolean(false),
|
||||
})`)
|
||||
|
||||
//go:wasmimport _gotest add
|
||||
func testAdd(uint32, uint32) uint32
|
||||
|
||||
func TestWasmImport(t *testing.T) {
|
||||
a := uint32(3)
|
||||
b := uint32(5)
|
||||
want := a + b
|
||||
if got := testAdd(a, b); got != want {
|
||||
t.Errorf("got %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
want := true
|
||||
o := dummys.Get("someBool")
|
||||
if got := o.Bool(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
dummys.Set("otherBool", want)
|
||||
if got := dummys.Get("otherBool").Bool(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if !dummys.Get("someBool").Equal(dummys.Get("someBool")) {
|
||||
t.Errorf("same value not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
want := "abc\u1234"
|
||||
o := dummys.Get("someString")
|
||||
if got := o.String(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
dummys.Set("otherString", want)
|
||||
if got := dummys.Get("otherString").String(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if !dummys.Get("someString").Equal(dummys.Get("someString")) {
|
||||
t.Errorf("same value not equal")
|
||||
}
|
||||
|
||||
if got, want := js.Undefined().String(), "<undefined>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := js.Null().String(), "<null>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := js.ValueOf(true).String(), "<boolean: true>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := js.ValueOf(42.5).String(), "<number: 42.5>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := js.Global().Call("Symbol").String(), "<symbol>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := js.Global().String(), "<object>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := js.Global().Get("setTimeout").String(), "<function>"; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
want := 42
|
||||
o := dummys.Get("someInt")
|
||||
if got := o.Int(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
dummys.Set("otherInt", want)
|
||||
if got := dummys.Get("otherInt").Int(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if !dummys.Get("someInt").Equal(dummys.Get("someInt")) {
|
||||
t.Errorf("same value not equal")
|
||||
}
|
||||
if got := dummys.Get("zero").Int(); got != 0 {
|
||||
t.Errorf("got %#v, want %#v", got, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIntConversion(t *testing.T) {
|
||||
testIntConversion(t, 0)
|
||||
testIntConversion(t, 1)
|
||||
testIntConversion(t, -1)
|
||||
testIntConversion(t, 1<<20)
|
||||
testIntConversion(t, -1<<20)
|
||||
testIntConversion(t, 1<<40)
|
||||
testIntConversion(t, -1<<40)
|
||||
testIntConversion(t, 1<<60)
|
||||
testIntConversion(t, -1<<60)
|
||||
}
|
||||
|
||||
func testIntConversion(t *testing.T, want int) {
|
||||
if got := js.ValueOf(want).Int(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFloat(t *testing.T) {
|
||||
want := 42.123
|
||||
o := dummys.Get("someFloat")
|
||||
if got := o.Float(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
dummys.Set("otherFloat", want)
|
||||
if got := dummys.Get("otherFloat").Float(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) {
|
||||
t.Errorf("same value not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestObject(t *testing.T) {
|
||||
if !dummys.Get("someArray").Equal(dummys.Get("someArray")) {
|
||||
t.Errorf("same value not equal")
|
||||
}
|
||||
|
||||
// An object and its prototype should not be equal.
|
||||
proto := js.Global().Get("Object").Get("prototype")
|
||||
o := js.Global().Call("eval", "new Object()")
|
||||
if proto.Equal(o) {
|
||||
t.Errorf("object equals to its prototype")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFrozenObject(t *testing.T) {
|
||||
o := js.Global().Call("eval", "(function () { let o = new Object(); o.field = 5; Object.freeze(o); return o; })()")
|
||||
want := 5
|
||||
if got := o.Get("field").Int(); want != got {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEqual(t *testing.T) {
|
||||
if !dummys.Get("someFloat").Equal(dummys.Get("someFloat")) {
|
||||
t.Errorf("same float is not equal")
|
||||
}
|
||||
if !dummys.Get("emptyObj").Equal(dummys.Get("emptyObj")) {
|
||||
t.Errorf("same object is not equal")
|
||||
}
|
||||
if dummys.Get("someFloat").Equal(dummys.Get("someInt")) {
|
||||
t.Errorf("different values are not unequal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNaN(t *testing.T) {
|
||||
if !dummys.Get("NaN").IsNaN() {
|
||||
t.Errorf("JS NaN is not NaN")
|
||||
}
|
||||
if !js.ValueOf(math.NaN()).IsNaN() {
|
||||
t.Errorf("Go NaN is not NaN")
|
||||
}
|
||||
if dummys.Get("NaN").Equal(dummys.Get("NaN")) {
|
||||
t.Errorf("NaN is equal to NaN")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUndefined(t *testing.T) {
|
||||
if !js.Undefined().IsUndefined() {
|
||||
t.Errorf("undefined is not undefined")
|
||||
}
|
||||
if !js.Undefined().Equal(js.Undefined()) {
|
||||
t.Errorf("undefined is not equal to undefined")
|
||||
}
|
||||
if dummys.IsUndefined() {
|
||||
t.Errorf("object is undefined")
|
||||
}
|
||||
if js.Undefined().IsNull() {
|
||||
t.Errorf("undefined is null")
|
||||
}
|
||||
if dummys.Set("test", js.Undefined()); !dummys.Get("test").IsUndefined() {
|
||||
t.Errorf("could not set undefined")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNull(t *testing.T) {
|
||||
if !js.Null().IsNull() {
|
||||
t.Errorf("null is not null")
|
||||
}
|
||||
if !js.Null().Equal(js.Null()) {
|
||||
t.Errorf("null is not equal to null")
|
||||
}
|
||||
if dummys.IsNull() {
|
||||
t.Errorf("object is null")
|
||||
}
|
||||
if js.Null().IsUndefined() {
|
||||
t.Errorf("null is undefined")
|
||||
}
|
||||
if dummys.Set("test", js.Null()); !dummys.Get("test").IsNull() {
|
||||
t.Errorf("could not set null")
|
||||
}
|
||||
if dummys.Set("test", nil); !dummys.Get("test").IsNull() {
|
||||
t.Errorf("could not set nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLength(t *testing.T) {
|
||||
if got := dummys.Get("someArray").Length(); got != 3 {
|
||||
t.Errorf("got %#v, want %#v", got, 3)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
// positive cases get tested per type
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").Get("badField")
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
// positive cases get tested per type
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").Set("badField", 42)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
dummys.Set("test", 42)
|
||||
dummys.Delete("test")
|
||||
if dummys.Call("hasOwnProperty", "test").Bool() {
|
||||
t.Errorf("property still exists")
|
||||
}
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").Delete("badField")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIndex(t *testing.T) {
|
||||
if got := dummys.Get("someArray").Index(1).Int(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").Index(1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSetIndex(t *testing.T) {
|
||||
dummys.Get("someArray").SetIndex(2, 99)
|
||||
if got := dummys.Get("someArray").Index(2).Int(); got != 99 {
|
||||
t.Errorf("got %#v, want %#v", got, 99)
|
||||
}
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").SetIndex(2, 99)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCall(t *testing.T) {
|
||||
var i int64 = 40
|
||||
if got := dummys.Call("add", i, 2).Int(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
if got := dummys.Call("add", js.Global().Call("eval", "40"), 2).Int(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
|
||||
expectPanic(t, func() {
|
||||
dummys.Call("zero")
|
||||
})
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").Call("badMethod")
|
||||
})
|
||||
}
|
||||
|
||||
func TestInvoke(t *testing.T) {
|
||||
var i int64 = 40
|
||||
if got := dummys.Get("add").Invoke(i, 2).Int(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").Invoke()
|
||||
})
|
||||
}
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
if got := js.Global().Get("Array").New(42).Length(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
|
||||
expectValueError(t, func() {
|
||||
dummys.Get("zero").New()
|
||||
})
|
||||
}
|
||||
|
||||
func TestInstanceOf(t *testing.T) {
|
||||
someArray := js.Global().Get("Array").New()
|
||||
if got, want := someArray.InstanceOf(js.Global().Get("Array")), true; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got, want := someArray.InstanceOf(js.Global().Get("Function")), false; got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestType(t *testing.T) {
|
||||
if got, want := js.Undefined().Type(), js.TypeUndefined; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.Null().Type(), js.TypeNull; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.ValueOf(true).Type(), js.TypeBoolean; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.ValueOf(0).Type(), js.TypeNumber; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.ValueOf(42).Type(), js.TypeNumber; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.ValueOf("test").Type(), js.TypeString; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.Global().Get("Symbol").Invoke("test").Type(), js.TypeSymbol; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.Global().Get("Array").New().Type(), js.TypeObject; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
if got, want := js.Global().Get("Array").Type(), js.TypeFunction; got != want {
|
||||
t.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
type object = map[string]any
|
||||
type array = []any
|
||||
|
||||
func TestValueOf(t *testing.T) {
|
||||
a := js.ValueOf(array{0, array{0, 42, 0}, 0})
|
||||
if got := a.Index(1).Index(1).Int(); got != 42 {
|
||||
t.Errorf("got %v, want %v", got, 42)
|
||||
}
|
||||
|
||||
o := js.ValueOf(object{"x": object{"y": 42}})
|
||||
if got := o.Get("x").Get("y").Int(); got != 42 {
|
||||
t.Errorf("got %v, want %v", got, 42)
|
||||
}
|
||||
}
|
||||
|
||||
func TestZeroValue(t *testing.T) {
|
||||
var v js.Value
|
||||
if !v.IsUndefined() {
|
||||
t.Error("zero js.Value is not js.Undefined()")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFuncOf(t *testing.T) {
|
||||
c := make(chan struct{})
|
||||
cb := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
if got := args[0].Int(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
c <- struct{}{}
|
||||
return nil
|
||||
})
|
||||
defer cb.Release()
|
||||
js.Global().Call("setTimeout", cb, 0, 42)
|
||||
<-c
|
||||
}
|
||||
|
||||
func TestInvokeFunction(t *testing.T) {
|
||||
called := false
|
||||
cb := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
cb2 := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
called = true
|
||||
return 42
|
||||
})
|
||||
defer cb2.Release()
|
||||
return cb2.Invoke()
|
||||
})
|
||||
defer cb.Release()
|
||||
if got := cb.Invoke().Int(); got != 42 {
|
||||
t.Errorf("got %#v, want %#v", got, 42)
|
||||
}
|
||||
if !called {
|
||||
t.Error("function not called")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInterleavedFunctions(t *testing.T) {
|
||||
c1 := make(chan struct{})
|
||||
c2 := make(chan struct{})
|
||||
|
||||
js.Global().Get("setTimeout").Invoke(js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
c1 <- struct{}{}
|
||||
<-c2
|
||||
return nil
|
||||
}), 0)
|
||||
|
||||
<-c1
|
||||
c2 <- struct{}{}
|
||||
// this goroutine is running, but the callback of setTimeout did not return yet, invoke another function now
|
||||
f := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
return nil
|
||||
})
|
||||
f.Invoke()
|
||||
}
|
||||
|
||||
func ExampleFuncOf() {
|
||||
var cb js.Func
|
||||
cb = js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
fmt.Println("button clicked")
|
||||
cb.Release() // release the function if the button will not be clicked again
|
||||
return nil
|
||||
})
|
||||
js.Global().Get("document").Call("getElementById", "myButton").Call("addEventListener", "click", cb)
|
||||
}
|
||||
|
||||
// See
|
||||
// - https://developer.mozilla.org/en-US/docs/Glossary/Truthy
|
||||
// - https://stackoverflow.com/questions/19839952/all-falsey-values-in-javascript/19839953#19839953
|
||||
// - http://www.ecma-international.org/ecma-262/5.1/#sec-9.2
|
||||
func TestTruthy(t *testing.T) {
|
||||
want := true
|
||||
for _, key := range []string{
|
||||
"someBool", "someString", "someInt", "someFloat", "someArray", "someDate",
|
||||
"stringZero", // "0" is truthy
|
||||
"add", // functions are truthy
|
||||
"emptyObj", "emptyArray", "Infinity", "NegInfinity",
|
||||
// All objects are truthy, even if they're Number(0) or Boolean(false).
|
||||
"objNumber0", "objBooleanFalse",
|
||||
} {
|
||||
if got := dummys.Get(key).Truthy(); got != want {
|
||||
t.Errorf("%s: got %#v, want %#v", key, got, want)
|
||||
}
|
||||
}
|
||||
|
||||
want = false
|
||||
if got := dummys.Get("zero").Truthy(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got := dummys.Get("NaN").Truthy(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got := js.ValueOf("").Truthy(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got := js.Null().Truthy(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
if got := js.Undefined().Truthy(); got != want {
|
||||
t.Errorf("got %#v, want %#v", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func expectValueError(t *testing.T, fn func()) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if _, ok := err.(*js.ValueError); !ok {
|
||||
t.Errorf("expected *js.ValueError, got %T", err)
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}
|
||||
|
||||
func expectPanic(t *testing.T, fn func()) {
|
||||
defer func() {
|
||||
err := recover()
|
||||
if err == nil {
|
||||
t.Errorf("expected panic")
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}
|
||||
|
||||
var copyTests = []struct {
|
||||
srcLen int
|
||||
dstLen int
|
||||
copyLen int
|
||||
}{
|
||||
{5, 3, 3},
|
||||
{3, 5, 3},
|
||||
{0, 0, 0},
|
||||
}
|
||||
|
||||
func TestCopyBytesToGo(t *testing.T) {
|
||||
for _, tt := range copyTests {
|
||||
t.Run(fmt.Sprintf("%d-to-%d", tt.srcLen, tt.dstLen), func(t *testing.T) {
|
||||
src := js.Global().Get("Uint8Array").New(tt.srcLen)
|
||||
if tt.srcLen >= 2 {
|
||||
src.SetIndex(1, 42)
|
||||
}
|
||||
dst := make([]byte, tt.dstLen)
|
||||
|
||||
if got, want := js.CopyBytesToGo(dst, src), tt.copyLen; got != want {
|
||||
t.Errorf("copied %d, want %d", got, want)
|
||||
}
|
||||
if tt.dstLen >= 2 {
|
||||
if got, want := int(dst[1]), 42; got != want {
|
||||
t.Errorf("got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyBytesToJS(t *testing.T) {
|
||||
for _, tt := range copyTests {
|
||||
t.Run(fmt.Sprintf("%d-to-%d", tt.srcLen, tt.dstLen), func(t *testing.T) {
|
||||
src := make([]byte, tt.srcLen)
|
||||
if tt.srcLen >= 2 {
|
||||
src[1] = 42
|
||||
}
|
||||
dst := js.Global().Get("Uint8Array").New(tt.dstLen)
|
||||
|
||||
if got, want := js.CopyBytesToJS(dst, src), tt.copyLen; got != want {
|
||||
t.Errorf("copied %d, want %d", got, want)
|
||||
}
|
||||
if tt.dstLen >= 2 {
|
||||
if got, want := dst.Index(1).Int(), 42; got != want {
|
||||
t.Errorf("got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGarbageCollection(t *testing.T) {
|
||||
before := js.JSGo.Get("_values").Length()
|
||||
for i := 0; i < 1000; i++ {
|
||||
_ = js.Global().Get("Object").New().Call("toString").String()
|
||||
runtime.GC()
|
||||
}
|
||||
after := js.JSGo.Get("_values").Length()
|
||||
if after-before > 500 {
|
||||
t.Errorf("garbage collection ineffective")
|
||||
}
|
||||
}
|
||||
|
||||
// This table is used for allocation tests. We expect a specific allocation
|
||||
// behavior to be seen, depending on the number of arguments applied to various
|
||||
// JavaScript functions.
|
||||
// Note: All JavaScript functions return a JavaScript array, which will cause
|
||||
// one allocation to be created to track the Value.gcPtr for the Value finalizer.
|
||||
var allocTests = []struct {
|
||||
argLen int // The number of arguments to use for the syscall
|
||||
expected int // The expected number of allocations
|
||||
}{
|
||||
// For less than or equal to 16 arguments, we expect 1 alloction:
|
||||
// - makeValue new(ref)
|
||||
{0, 1},
|
||||
{2, 1},
|
||||
{15, 1},
|
||||
{16, 1},
|
||||
// For greater than 16 arguments, we expect 3 alloction:
|
||||
// - makeValue: new(ref)
|
||||
// - makeArgSlices: argVals = make([]Value, size)
|
||||
// - makeArgSlices: argRefs = make([]ref, size)
|
||||
{17, 3},
|
||||
{32, 3},
|
||||
{42, 3},
|
||||
}
|
||||
|
||||
// TestCallAllocations ensures the correct allocation profile for Value.Call
|
||||
func TestCallAllocations(t *testing.T) {
|
||||
for _, test := range allocTests {
|
||||
args := make([]any, test.argLen)
|
||||
|
||||
tmpArray := js.Global().Get("Array").New(0)
|
||||
numAllocs := testing.AllocsPerRun(100, func() {
|
||||
tmpArray.Call("concat", args...)
|
||||
});
|
||||
|
||||
if numAllocs != float64(test.expected) {
|
||||
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInvokeAllocations ensures the correct allocation profile for Value.Invoke
|
||||
func TestInvokeAllocations(t *testing.T) {
|
||||
for _, test := range allocTests {
|
||||
args := make([]any, test.argLen)
|
||||
|
||||
tmpArray := js.Global().Get("Array").New(0)
|
||||
concatFunc := tmpArray.Get("concat").Call("bind", tmpArray)
|
||||
numAllocs := testing.AllocsPerRun(100, func() {
|
||||
concatFunc.Invoke(args...)
|
||||
});
|
||||
|
||||
if numAllocs != float64(test.expected) {
|
||||
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewAllocations ensures the correct allocation profile for Value.New
|
||||
func TestNewAllocations(t *testing.T) {
|
||||
arrayConstructor := js.Global().Get("Array")
|
||||
|
||||
for _, test := range allocTests {
|
||||
args := make([]any, test.argLen)
|
||||
|
||||
numAllocs := testing.AllocsPerRun(100, func() {
|
||||
arrayConstructor.New(args...)
|
||||
});
|
||||
|
||||
if numAllocs != float64(test.expected) {
|
||||
t.Errorf("got numAllocs %#v, want %#v", numAllocs, test.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkDOM is a simple benchmark which emulates a webapp making DOM operations.
|
||||
// It creates a div, and sets its id. Then searches by that id and sets some data.
|
||||
// Finally it removes that div.
|
||||
func BenchmarkDOM(b *testing.B) {
|
||||
document := js.Global().Get("document")
|
||||
if document.IsUndefined() {
|
||||
b.Skip("Not a browser environment. Skipping.")
|
||||
}
|
||||
const data = "someString"
|
||||
for i := 0; i < b.N; i++ {
|
||||
div := document.Call("createElement", "div")
|
||||
div.Call("setAttribute", "id", "myDiv")
|
||||
document.Get("body").Call("appendChild", div)
|
||||
myDiv := document.Call("getElementById", "myDiv")
|
||||
myDiv.Set("innerHTML", data)
|
||||
|
||||
if got, want := myDiv.Get("innerHTML").String(), data; got != want {
|
||||
b.Errorf("got %s, want %s", got, want)
|
||||
}
|
||||
document.Get("body").Call("removeChild", div)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGlobal(t *testing.T) {
|
||||
ident := js.FuncOf(func(this js.Value, args []js.Value) any {
|
||||
return args[0]
|
||||
})
|
||||
defer ident.Release()
|
||||
|
||||
if got := ident.Invoke(js.Global()); !got.Equal(js.Global()) {
|
||||
t.Errorf("got %#v, want %#v", got, js.Global())
|
||||
}
|
||||
}
|
||||
17
src/syscall/linkname_bsd.go
Normal file
17
src/syscall/linkname_bsd.go
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
||||
|
||||
package syscall
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// used by internal/syscall/unix
|
||||
//go:linkname ioctlPtr
|
||||
|
||||
// golang.org/x/net linknames sysctl.
|
||||
// Do not remove or change the type signature.
|
||||
//
|
||||
//go:linkname sysctl
|
||||
23
src/syscall/linkname_darwin.go
Normal file
23
src/syscall/linkname_darwin.go
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package syscall
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// used by os
|
||||
//go:linkname closedir
|
||||
//go:linkname readdir_r
|
||||
|
||||
// used by internal/poll
|
||||
//go:linkname fdopendir
|
||||
|
||||
// used by internal/syscall/unix
|
||||
//go:linkname unlinkat
|
||||
//go:linkname openat
|
||||
//go:linkname fstatat
|
||||
|
||||
// used by cmd/link
|
||||
//go:linkname msync
|
||||
//go:linkname fcntl
|
||||
12
src/syscall/linkname_libc.go
Normal file
12
src/syscall/linkname_libc.go
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build aix || darwin || (openbsd && !mips64) || solaris
|
||||
|
||||
package syscall
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// used by internal/poll
|
||||
//go:linkname writev
|
||||
15
src/syscall/linkname_openbsd.go
Normal file
15
src/syscall/linkname_openbsd.go
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build openbsd && !mips64
|
||||
|
||||
package syscall
|
||||
|
||||
import _ "unsafe"
|
||||
|
||||
// used by internal/syscall/unix
|
||||
//go:linkname unlinkat
|
||||
//go:linkname openat
|
||||
//go:linkname fstatat
|
||||
//go:linkname getentropy
|
||||
20
src/syscall/linkname_unix.go
Normal file
20
src/syscall/linkname_unix.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2024 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build unix
|
||||
|
||||
package syscall
|
||||
|
||||
import _ "unsafe" // for linkname
|
||||
|
||||
// mmap should be an internal detail,
|
||||
// but widely used packages access it using linkname.
|
||||
// Notable members of the hall of shame include:
|
||||
// - modernc.org/memory
|
||||
// - github.com/ncruces/go-sqlite3
|
||||
//
|
||||
// Do not remove or change the type signature.
|
||||
// See go.dev/issue/67401.
|
||||
//
|
||||
//go:linkname mmap
|
||||
86
src/syscall/lsf_linux.go
Normal file
86
src/syscall/lsf_linux.go
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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.
|
||||
|
||||
// Linux socket filter
|
||||
|
||||
package syscall
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func LsfStmt(code, k int) *SockFilter {
|
||||
return &SockFilter{Code: uint16(code), K: uint32(k)}
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func LsfJump(code, k, jt, jf int) *SockFilter {
|
||||
return &SockFilter{Code: uint16(code), Jt: uint8(jt), Jf: uint8(jf), K: uint32(k)}
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func LsfSocket(ifindex, proto int) (int, error) {
|
||||
var lsall SockaddrLinklayer
|
||||
// This is missing SOCK_CLOEXEC, but adding the flag
|
||||
// could break callers.
|
||||
s, e := Socket(AF_PACKET, SOCK_RAW, proto)
|
||||
if e != nil {
|
||||
return 0, e
|
||||
}
|
||||
p := (*[2]byte)(unsafe.Pointer(&lsall.Protocol))
|
||||
p[0] = byte(proto >> 8)
|
||||
p[1] = byte(proto)
|
||||
lsall.Ifindex = ifindex
|
||||
e = Bind(s, &lsall)
|
||||
if e != nil {
|
||||
Close(s)
|
||||
return 0, e
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
type iflags struct {
|
||||
name [IFNAMSIZ]byte
|
||||
flags uint16
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func SetLsfPromisc(name string, m bool) error {
|
||||
s, e := Socket(AF_INET, SOCK_DGRAM|SOCK_CLOEXEC, 0)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
defer Close(s)
|
||||
var ifl iflags
|
||||
copy(ifl.name[:], []byte(name))
|
||||
_, _, ep := Syscall(SYS_IOCTL, uintptr(s), SIOCGIFFLAGS, uintptr(unsafe.Pointer(&ifl)))
|
||||
if ep != 0 {
|
||||
return Errno(ep)
|
||||
}
|
||||
if m {
|
||||
ifl.flags |= uint16(IFF_PROMISC)
|
||||
} else {
|
||||
ifl.flags &^= uint16(IFF_PROMISC)
|
||||
}
|
||||
_, _, ep = Syscall(SYS_IOCTL, uintptr(s), SIOCSIFFLAGS, uintptr(unsafe.Pointer(&ifl)))
|
||||
if ep != 0 {
|
||||
return Errno(ep)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func AttachLsf(fd int, i []SockFilter) error {
|
||||
var p SockFprog
|
||||
p.Len = uint16(len(i))
|
||||
p.Filter = (*SockFilter)(unsafe.Pointer(&i[0]))
|
||||
return setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, unsafe.Pointer(&p), unsafe.Sizeof(p))
|
||||
}
|
||||
|
||||
// Deprecated: Use golang.org/x/net/bpf instead.
|
||||
func DetachLsf(fd int) error {
|
||||
var dummy int
|
||||
return setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, unsafe.Pointer(&dummy), unsafe.Sizeof(dummy))
|
||||
}
|
||||
422
src/syscall/mkall.sh
Executable file
422
src/syscall/mkall.sh
Executable file
@@ -0,0 +1,422 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
# The syscall package provides access to the raw system call
|
||||
# interface of the underlying operating system. Porting Go to
|
||||
# a new architecture/operating system combination requires
|
||||
# some manual effort, though there are tools that automate
|
||||
# much of the process. The auto-generated files have names
|
||||
# beginning with z.
|
||||
#
|
||||
# This script runs or (given -n) prints suggested commands to generate z files
|
||||
# for the current system. Running those commands is not automatic.
|
||||
# This script is documentation more than anything else.
|
||||
#
|
||||
# * asm_${GOOS}_${GOARCH}.s
|
||||
#
|
||||
# This hand-written assembly file implements system call dispatch.
|
||||
# There are three entry points:
|
||||
#
|
||||
# func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
|
||||
# func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr);
|
||||
# func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr);
|
||||
#
|
||||
# The first and second are the standard ones; they differ only in
|
||||
# how many arguments can be passed to the kernel.
|
||||
# The third is for low-level use by the ForkExec wrapper;
|
||||
# unlike the first two, it does not call into the scheduler to
|
||||
# let it know that a system call is running.
|
||||
#
|
||||
# * syscall_${GOOS}.go
|
||||
#
|
||||
# This hand-written Go file implements system calls that need
|
||||
# special handling and lists "//sys" comments giving prototypes
|
||||
# for ones that can be auto-generated. Mksyscall reads those
|
||||
# comments to generate the stubs.
|
||||
#
|
||||
# * syscall_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Same as syscall_${GOOS}.go except that it contains code specific
|
||||
# to ${GOOS} on one particular architecture.
|
||||
#
|
||||
# * types_${GOOS}.c
|
||||
#
|
||||
# This hand-written C file includes standard C headers and then
|
||||
# creates typedef or enum names beginning with a dollar sign
|
||||
# (use of $ in variable names is a gcc extension). The hardest
|
||||
# part about preparing this file is figuring out which headers to
|
||||
# include and which symbols need to be #defined to get the
|
||||
# actual data structures that pass through to the kernel system calls.
|
||||
# Some C libraries present alternate versions for binary compatibility
|
||||
# and translate them on the way in and out of system calls, but
|
||||
# there is almost always a #define that can get the real ones.
|
||||
# See types_darwin.c and types_linux.c for examples.
|
||||
#
|
||||
# * zerror_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# This machine-generated file defines the system's error numbers,
|
||||
# error strings, and signal numbers. The generator is "mkerrors.sh".
|
||||
# Usually no arguments are needed, but mkerrors.sh will pass its
|
||||
# arguments on to godefs.
|
||||
#
|
||||
# * zsyscall_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by mksyscall.pl; see syscall_${GOOS}.go above.
|
||||
#
|
||||
# * zsysnum_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by mksysnum_${GOOS}.
|
||||
#
|
||||
# * ztypes_${GOOS}_${GOARCH}.go
|
||||
#
|
||||
# Generated by godefs; see types_${GOOS}.c above.
|
||||
|
||||
GOOSARCH="${GOOS}_${GOARCH}"
|
||||
|
||||
# defaults
|
||||
mksyscall="./mksyscall.pl"
|
||||
mkerrors="./mkerrors.sh"
|
||||
zerrors="zerrors_$GOOSARCH.go"
|
||||
mksysctl=""
|
||||
zsysctl="zsysctl_$GOOSARCH.go"
|
||||
mksysnum=
|
||||
mktypes=
|
||||
mkasm=
|
||||
run="sh"
|
||||
|
||||
case "$1" in
|
||||
-syscalls)
|
||||
shift
|
||||
for i in ${@:-zsyscall*go}
|
||||
do
|
||||
# Run the command line that appears in the first line
|
||||
# of the generated file to regenerate it.
|
||||
sed 1q $i | sed 's;^// ;./;' | sh > _$i && gofmt < _$i > $i
|
||||
rm _$i
|
||||
done
|
||||
exit 0
|
||||
;;
|
||||
-n)
|
||||
run="cat"
|
||||
shift
|
||||
esac
|
||||
|
||||
case "$#" in
|
||||
0)
|
||||
;;
|
||||
*)
|
||||
echo 'usage: mkall.sh [-n]' 1>&2
|
||||
exit 2
|
||||
esac
|
||||
|
||||
GOOSARCH_in=syscall_$GOOSARCH.go
|
||||
case "$GOOSARCH" in
|
||||
_* | *_ | _)
|
||||
echo 'undefined $GOOS_$GOARCH:' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
aix_ppc64)
|
||||
mkerrors="$mkerrors -maix64"
|
||||
mksyscall="./mksyscall_libc.pl -aix"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
darwin_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -darwin"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
darwin_arm64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -darwin"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
dragonfly_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -dragonfly"
|
||||
mksysnum="curl -s 'http://gitweb.dragonflybsd.org/dragonfly.git/blob_plain/HEAD:/sys/kern/syscalls.master' | ./mksysnum_dragonfly.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
freebsd_386)
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="./mksyscall.pl -l32"
|
||||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
freebsd_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
freebsd_arm)
|
||||
mkerrors="$mkerrors"
|
||||
mksyscall="./mksyscall.pl -l32 -arm"
|
||||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
freebsd_arm64)
|
||||
mkerrors="$mkerrors"
|
||||
mksysnum="curl -s 'http://svn.freebsd.org/base/stable/10/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
freebsd_riscv64)
|
||||
mkerrors="$mkerrors"
|
||||
mksysnum="curl -s 'https://cgit.freebsd.org/src/plain/sys/kern/syscalls.master?h=stable/12' | ./mksysnum_freebsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
linux_386)
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="./mksyscall.pl -l32"
|
||||
mksysnum="./mksysnum_linux.pl /usr/include/asm/unistd_32.h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_amd64)
|
||||
unistd_h=$(ls -1 /usr/include/asm/unistd_64.h /usr/include/x86_64-linux-gnu/asm/unistd_64.h 2>/dev/null | head -1)
|
||||
if [ "$unistd_h" = "" ]; then
|
||||
echo >&2 cannot find unistd_64.h
|
||||
exit 1
|
||||
fi
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_arm)
|
||||
GOOSARCH_in="syscall_linux_arm.go syscall_linux_accept.go"
|
||||
mkerrors="$mkerrors"
|
||||
mksyscall="./mksyscall.pl -l32 -arm"
|
||||
mksysnum="curl -s 'http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/plain/arch/arm/include/uapi/asm/unistd.h' | ./mksysnum_linux.pl -"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_arm64)
|
||||
unistd_h=$(ls -1 /usr/include/asm/unistd.h /usr/include/asm-generic/unistd.h 2>/dev/null | head -1)
|
||||
if [ "$unistd_h" = "" ]; then
|
||||
echo >&2 cannot find unistd_64.h
|
||||
exit 1
|
||||
fi
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
linux_loong64)
|
||||
GOOSARCH_in=syscall_linux_loong64.go
|
||||
unistd_h=$(ls -1 /usr/include/asm/unistd.h /usr/include/asm-generic/unistd.h 2>/dev/null | head -1)
|
||||
if [ "$unistd_h" = "" ]; then
|
||||
echo >&2 cannot find unistd.h
|
||||
exit 1
|
||||
fi
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_mips)
|
||||
GOOSARCH_in=syscall_linux_mipsx.go
|
||||
unistd_h=/usr/include/asm/unistd.h
|
||||
mksyscall="./mksyscall.pl -b32 -arm"
|
||||
mkerrors="$mkerrors"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_mipsle)
|
||||
GOOSARCH_in=syscall_linux_mipsx.go
|
||||
unistd_h=/usr/include/asm/unistd.h
|
||||
mksyscall="./mksyscall.pl -l32 -arm"
|
||||
mkerrors="$mkerrors"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_mips64)
|
||||
GOOSARCH_in=syscall_linux_mips64x.go
|
||||
unistd_h=/usr/include/asm/unistd.h
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_mips64le)
|
||||
GOOSARCH_in=syscall_linux_mips64x.go
|
||||
unistd_h=/usr/include/asm/unistd.h
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_ppc64)
|
||||
GOOSARCH_in=syscall_linux_ppc64x.go
|
||||
unistd_h=/usr/include/asm/unistd.h
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_ppc64le)
|
||||
GOOSARCH_in=syscall_linux_ppc64x.go
|
||||
unistd_h=/usr/include/powerpc64le-linux-gnu/asm/unistd.h
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum="./mksysnum_linux.pl $unistd_h"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
linux_riscv64)
|
||||
unistd_h=$(ls -1 /usr/include/asm/unistd.h /usr/include/asm-generic/unistd.h 2>/dev/null | head -1)
|
||||
if [ "$unistd_h" = "" ]; then
|
||||
echo >&2 cannot find unistd_64.h
|
||||
exit 1
|
||||
fi
|
||||
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"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
netbsd_386)
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="./mksyscall.pl -l32 -netbsd"
|
||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
netbsd_amd64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -netbsd"
|
||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
netbsd_arm)
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="./mksyscall.pl -l32 -netbsd -arm"
|
||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
netbsd_arm64)
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -netbsd"
|
||||
mksysnum="curl -s 'http://cvsweb.netbsd.org/bsdweb.cgi/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_netbsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
openbsd_386)
|
||||
GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors -m32"
|
||||
mksyscall="./mksyscall.pl -l32 -openbsd -libc"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
openbsd_amd64)
|
||||
GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -openbsd -libc"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
openbsd_arm)
|
||||
GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors"
|
||||
mksyscall="./mksyscall.pl -l32 -openbsd -arm -libc"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
openbsd_arm64)
|
||||
GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -openbsd -libc"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
openbsd_mips64)
|
||||
GOOSARCH_in="syscall_openbsd1.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -openbsd"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
;;
|
||||
openbsd_ppc64)
|
||||
GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -openbsd -libc"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
openbsd_riscv64)
|
||||
GOOSARCH_in="syscall_openbsd_libc.go syscall_openbsd_$GOARCH.go"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksyscall="./mksyscall.pl -openbsd -libc"
|
||||
mksysctl="./mksysctl_openbsd.pl"
|
||||
zsysctl="zsysctl_openbsd.go"
|
||||
mksysnum="curl -s 'http://cvsweb.openbsd.org/cgi-bin/cvsweb/~checkout~/src/sys/kern/syscalls.master' | ./mksysnum_openbsd.pl"
|
||||
# Let the type of C char be signed to make the bare syscall
|
||||
# API consistent between platforms.
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
|
||||
mkasm="go run mkasm.go"
|
||||
;;
|
||||
plan9_386)
|
||||
mkerrors=
|
||||
mksyscall="./mksyscall.pl -l32 -plan9"
|
||||
mksysnum="./mksysnum_plan9.sh /n/sources/plan9/sys/src/libc/9syscall/sys.h"
|
||||
mktypes="XXX"
|
||||
;;
|
||||
solaris_amd64)
|
||||
mksyscall="./mksyscall_libc.pl -solaris"
|
||||
mkerrors="$mkerrors -m64"
|
||||
mksysnum=
|
||||
mktypes="GOARCH=$GOARCH go tool cgo -godefs"
|
||||
;;
|
||||
windows_*)
|
||||
echo 'run "go generate" instead' 1>&2
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo 'unrecognized $GOOS_$GOARCH: ' "$GOOSARCH" 1>&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
(
|
||||
if [ -n "$mkerrors" ]; then echo "$mkerrors |gofmt >$zerrors"; fi
|
||||
syscall_goos="syscall_$GOOS.go"
|
||||
case "$GOOS" in
|
||||
darwin | dragonfly | freebsd | netbsd | openbsd)
|
||||
syscall_goos="syscall_bsd.go $syscall_goos"
|
||||
;;
|
||||
esac
|
||||
if [ -n "$mksyscall" ]; then echo "$mksyscall -tags $GOOS,$GOARCH $syscall_goos $GOOSARCH_in |gofmt >zsyscall_$GOOSARCH.go"; fi
|
||||
if [ -n "$mksysctl" ]; then echo "$mksysctl |gofmt >$zsysctl"; fi
|
||||
if [ -n "$mksysnum" ]; then echo "$mksysnum |gofmt >zsysnum_$GOOSARCH.go"; fi
|
||||
if [ -n "$mktypes" ]; then
|
||||
# ztypes_$GOOSARCH.go could be erased before "go run mkpost.go" is called.
|
||||
# Therefore, "go run" tries to recompile syscall package but ztypes is empty and it fails.
|
||||
echo "$mktypes types_$GOOS.go |go run mkpost.go >ztypes_$GOOSARCH.go.NEW && mv ztypes_$GOOSARCH.go.NEW ztypes_$GOOSARCH.go";
|
||||
fi
|
||||
if [ -n "$mkasm" ]; then echo "$mkasm $GOOS $GOARCH"; fi
|
||||
) | $run
|
||||
69
src/syscall/mkasm.go
Normal file
69
src/syscall/mkasm.go
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2018 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// mkasm.go generates assembly trampolines to call library routines from Go.
|
||||
// This program must be run after mksyscall.pl.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) != 3 {
|
||||
log.Fatalf("Usage: %s <goos> <arch>", os.Args[0])
|
||||
}
|
||||
goos, arch := os.Args[1], os.Args[2]
|
||||
|
||||
syscallFilename := fmt.Sprintf("syscall_%s.go", goos)
|
||||
syscallArchFilename := fmt.Sprintf("syscall_%s_%s.go", goos, arch)
|
||||
|
||||
in1, err := os.ReadFile(syscallFilename)
|
||||
if err != nil {
|
||||
log.Fatalf("can't open syscall file: %s", err)
|
||||
}
|
||||
in2, err := os.ReadFile(syscallArchFilename)
|
||||
if err != nil {
|
||||
log.Fatalf("can't open syscall file: %s", err)
|
||||
}
|
||||
in3, err := os.ReadFile("z" + syscallArchFilename)
|
||||
if err != nil {
|
||||
log.Fatalf("can't open syscall file: %s", err)
|
||||
}
|
||||
in := string(in1) + string(in2) + string(in3)
|
||||
|
||||
trampolines := map[string]bool{}
|
||||
|
||||
var out bytes.Buffer
|
||||
|
||||
fmt.Fprintf(&out, "// go run mkasm.go %s\n", strings.Join(os.Args[1:], " "))
|
||||
fmt.Fprintf(&out, "// Code generated by the command above; DO NOT EDIT.\n")
|
||||
fmt.Fprintf(&out, "#include \"textflag.h\"\n")
|
||||
for _, line := range strings.Split(in, "\n") {
|
||||
if !strings.HasPrefix(line, "func ") || !strings.HasSuffix(line, "_trampoline()") {
|
||||
continue
|
||||
}
|
||||
fn := line[5 : len(line)-13]
|
||||
if !trampolines[fn] {
|
||||
trampolines[fn] = true
|
||||
fmt.Fprintf(&out, "TEXT ·%s_trampoline(SB),NOSPLIT,$0-0\n", fn)
|
||||
if goos == "openbsd" && arch == "ppc64" {
|
||||
fmt.Fprintf(&out, "\tCALL\t%s(SB)\n", fn)
|
||||
fmt.Fprintf(&out, "\tRET\n")
|
||||
} else {
|
||||
fmt.Fprintf(&out, "\tJMP\t%s(SB)\n", fn)
|
||||
}
|
||||
}
|
||||
}
|
||||
err = os.WriteFile(fmt.Sprintf("zsyscall_%s_%s.s", goos, arch), out.Bytes(), 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("can't write syscall file: %s", err)
|
||||
}
|
||||
}
|
||||
462
src/syscall/mkerrors.sh
Executable file
462
src/syscall/mkerrors.sh
Executable file
@@ -0,0 +1,462 @@
|
||||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
# Generate Go code listing errors and other #defined constant
|
||||
# values (ENAMETOOLONG etc.), by asking the preprocessor
|
||||
# about the definitions.
|
||||
|
||||
unset LANG
|
||||
export LC_ALL=C
|
||||
export LC_CTYPE=C
|
||||
|
||||
CC=${CC:-gcc}
|
||||
|
||||
if [[ "$GOOS" -eq "solaris" ]]; then
|
||||
# Assumes GNU versions of utilities in PATH.
|
||||
export PATH=/usr/gnu/bin:$PATH
|
||||
fi
|
||||
|
||||
uname=$(uname)
|
||||
|
||||
includes_AIX='
|
||||
#include <net/if.h>
|
||||
#include <net/netopt.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/protosw.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/stropts.h>
|
||||
#include <termios.h>
|
||||
'
|
||||
|
||||
includes_Darwin='
|
||||
#define _DARWIN_C_SOURCE
|
||||
#define KERNEL
|
||||
#define _DARWIN_USE_64_BIT_INODE
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
#include <termios.h>
|
||||
'
|
||||
|
||||
includes_DragonFly='
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <termios.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <net/ip_mroute/ip_mroute.h>
|
||||
'
|
||||
|
||||
includes_FreeBSD='
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <termios.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
|
||||
#if __FreeBSD__ >= 10
|
||||
#define IFT_CARP 0xf8 // IFT_CARP is deprecated in FreeBSD 10
|
||||
#undef SIOCAIFADDR
|
||||
#define SIOCAIFADDR _IOW(105, 26, struct oifaliasreq) // ifaliasreq contains if_data
|
||||
#undef SIOCSIFPHYADDR
|
||||
#define SIOCSIFPHYADDR _IOW(105, 70, struct oifaliasreq) // ifaliasreq contains if_data
|
||||
#endif
|
||||
'
|
||||
|
||||
includes_Linux='
|
||||
#define _LARGEFILE_SOURCE
|
||||
#define _LARGEFILE64_SOURCE
|
||||
#ifndef __LP64__
|
||||
#define _FILE_OFFSET_BITS 64
|
||||
#endif
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <bits/sockaddr.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/if_tun.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_addr.h>
|
||||
#include <linux/filter.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/icmpv6.h>
|
||||
#include <net/route.h>
|
||||
#include <termios.h>
|
||||
|
||||
#ifndef MSG_FASTOPEN
|
||||
#define MSG_FASTOPEN 0x20000000
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_GETREGS
|
||||
#define PTRACE_GETREGS 0xc
|
||||
#endif
|
||||
|
||||
#ifndef PTRACE_SETREGS
|
||||
#define PTRACE_SETREGS 0xd
|
||||
#endif
|
||||
'
|
||||
|
||||
includes_NetBSD='
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/termios.h>
|
||||
#include <sys/ttycom.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
#include <netinet/if_ether.h>
|
||||
|
||||
// Needed since <sys/param.h> refers to it...
|
||||
#define schedppq 1
|
||||
'
|
||||
|
||||
includes_OpenBSD='
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/event.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/termios.h>
|
||||
#include <sys/ttycom.h>
|
||||
#include <sys/wait.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/if_var.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/in_systm.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
#include <netinet/if_ether.h>
|
||||
#include <net/if_bridge.h>
|
||||
|
||||
// We keep some constants not supported in OpenBSD 5.5 and beyond for
|
||||
// the promise of compatibility.
|
||||
#define EMUL_ENABLED 0x1
|
||||
#define EMUL_NATIVE 0x2
|
||||
#define IPV6_FAITH 0x1d
|
||||
#define IPV6_OPTIONS 0x1
|
||||
#define IPV6_RTHDR_STRICT 0x1
|
||||
#define IPV6_SOCKOPT_RESERVED1 0x3
|
||||
#define SIOCGIFGENERIC 0xc020693a
|
||||
#define SIOCSIFGENERIC 0x80206939
|
||||
#define WALTSIG 0x4
|
||||
'
|
||||
|
||||
includes_SunOS='
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/sockio.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <net/bpf.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/if_types.h>
|
||||
#include <net/route.h>
|
||||
#include <netinet/in.h>
|
||||
#include <termios.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip_mroute.h>
|
||||
'
|
||||
|
||||
includes='
|
||||
#include <sys/types.h>
|
||||
#include <sys/file.h>
|
||||
#include <fcntl.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <netinet/ip6.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <errno.h>
|
||||
#include <sys/signal.h>
|
||||
#include <signal.h>
|
||||
#include <sys/resource.h>
|
||||
'
|
||||
|
||||
ccflags="$@"
|
||||
|
||||
# Write go tool cgo -godefs input.
|
||||
(
|
||||
echo package syscall
|
||||
echo
|
||||
echo '/*'
|
||||
indirect="includes_$(uname)"
|
||||
echo "${!indirect} $includes"
|
||||
echo '*/'
|
||||
echo 'import "C"'
|
||||
echo
|
||||
echo 'const ('
|
||||
|
||||
# The gcc command line prints all the #defines
|
||||
# it encounters while processing the input
|
||||
echo "${!indirect} $includes" | $CC -x c - -E -dM $ccflags |
|
||||
awk '
|
||||
$1 != "#define" || $2 ~ /\(/ || $3 == "" {next}
|
||||
|
||||
$2 ~ /^E([ABCD]X|[BIS]P|[SD]I|S|FL)$/ {next} # 386 registers
|
||||
$2 ~ /^(SIGEV_|SIGSTKSZ|SIGRT(MIN|MAX))/ {next}
|
||||
$2 ~ /^(SCM_SRCRT)$/ {next}
|
||||
$2 ~ /^(MAP_FAILED)$/ {next}
|
||||
$2 ~ /^CLONE_[A-Z_]+/ {next} # These are defined in exec_linux.go.
|
||||
$2 ~ /^ELF_.*$/ {next} # <asm/elf.h> contains ELF_ARCH, etc.
|
||||
|
||||
$2 !~ /^ETH_/ &&
|
||||
$2 !~ /^EPROC_/ &&
|
||||
$2 !~ /^EQUIV_/ &&
|
||||
$2 !~ /^EXPR_/ &&
|
||||
$2 ~ /^E[A-Z0-9_]+$/ ||
|
||||
$2 ~ /^B[0-9_]+$/ ||
|
||||
$2 ~ /^V[A-Z0-9]+$/ ||
|
||||
$2 ~ /^CS[A-Z0-9]/ ||
|
||||
$2 ~ /^I(SIG|CANON|CRNL|EXTEN|MAXBEL|STRIP|UTF8)$/ ||
|
||||
$2 ~ /^IGN/ ||
|
||||
$2 ~ /^IX(ON|ANY|OFF)$/ ||
|
||||
$2 ~ /^IN(LCR|PCK)$/ ||
|
||||
$2 ~ /(^FLU?SH)|(FLU?SH$)/ ||
|
||||
$2 ~ /^C(LOCAL|READ)$/ ||
|
||||
$2 == "BRKINT" ||
|
||||
$2 == "HUPCL" ||
|
||||
$2 == "PENDIN" ||
|
||||
$2 == "TOSTOP" ||
|
||||
$2 ~ /^PAR/ ||
|
||||
$2 ~ /^SIG[^_]/ ||
|
||||
$2 ~ /^O[CNPFP][A-Z]+[^_][A-Z]+$/ ||
|
||||
$2 ~ /^IN_/ ||
|
||||
$2 ~ /^LOCK_(SH|EX|NB|UN)$/ ||
|
||||
$2 ~ /^(AF|SOCK|SO|SOL|IPPROTO|IP|IPV6|ICMP6|TCP|EVFILT|NOTE|EV|SHUT|PROT|MAP|PACKET|MSG|SCM|MCL|DT|MADV|PR)_/ ||
|
||||
$2 == "ICMPV6_FILTER" ||
|
||||
$2 == "SOMAXCONN" ||
|
||||
$2 == "NAME_MAX" ||
|
||||
$2 == "IFNAMSIZ" ||
|
||||
$2 ~ /^CTL_(MAXNAME|NET|QUERY)$/ ||
|
||||
$2 ~ /^SYSCTL_VERS/ ||
|
||||
$2 ~ /^(MS|MNT)_/ ||
|
||||
$2 ~ /^TUN(SET|GET|ATTACH|DETACH)/ ||
|
||||
$2 ~ /^(O|F|FD|NAME|S|PTRACE|PT)_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_CMD_/ ||
|
||||
$2 ~ /^LINUX_REBOOT_MAGIC[12]$/ ||
|
||||
$2 !~ "NLA_TYPE_MASK" &&
|
||||
$2 ~ /^(NETLINK|NLM|NLMSG|NLA|IFA|IFAN|RT|RTCF|RTN|RTPROT|RTNH|ARPHRD|ETH_P)_/ ||
|
||||
$2 ~ /^SIOC/ ||
|
||||
$2 ~ /^TIOC/ ||
|
||||
$2 !~ "RTF_BITS" &&
|
||||
$2 ~ /^(IFF|IFT|NET_RT|RTM|RTF|RTV|RTA|RTAX)_/ ||
|
||||
$2 ~ /^BIOC/ ||
|
||||
$2 ~ /^RUSAGE_(SELF|CHILDREN|THREAD)/ ||
|
||||
$2 ~ /^RLIMIT_(AS|CORE|CPU|DATA|FSIZE|NOFILE|STACK)|RLIM_INFINITY/ ||
|
||||
$2 ~ /^PRIO_(PROCESS|PGRP|USER)/ ||
|
||||
$2 !~ /^(BPF_TIMEVAL)$/ &&
|
||||
$2 ~ /^(BPF|DLT)_/ ||
|
||||
$2 !~ "WMESGLEN" &&
|
||||
$2 ~ /^W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", $2, $2)}
|
||||
$2 ~ /^__WCOREFLAG$/ {next}
|
||||
$2 ~ /^__W[A-Z0-9]+$/ {printf("\t%s = C.%s\n", substr($2,3), $2)}
|
||||
|
||||
{next}
|
||||
' | sort
|
||||
|
||||
echo ')'
|
||||
) >_const.go
|
||||
|
||||
# Pull out the error names for later.
|
||||
errors=$(
|
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print $2 }' |
|
||||
sort
|
||||
)
|
||||
|
||||
# Pull out the signal names for later.
|
||||
signals=$(
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print $2 }' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' |
|
||||
sort
|
||||
)
|
||||
|
||||
# Again, writing regexps to a file.
|
||||
echo '#include <errno.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^E[A-Z0-9_]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
sort >_error.grep
|
||||
echo '#include <signal.h>' | $CC -x c - -E -dM $ccflags |
|
||||
awk '$1=="#define" && $2 ~ /^SIG[A-Z0-9]+$/ { print "^\t" $2 "[ \t]*=" }' |
|
||||
grep -v 'SIGSTKSIZE\|SIGSTKSZ\|SIGRT' |
|
||||
sort >_signal.grep
|
||||
|
||||
echo '// mkerrors.sh' "$@"
|
||||
echo '// Code generated by the command above; DO NOT EDIT.'
|
||||
echo
|
||||
go tool cgo -godefs -- "$@" _const.go >_error.out
|
||||
cat _error.out | grep -vf _error.grep | grep -vf _signal.grep
|
||||
echo
|
||||
echo '// Errors'
|
||||
echo 'const ('
|
||||
cat _error.out | grep -f _error.grep | sed 's/=\(.*\)/= Errno(\1)/'
|
||||
echo ')'
|
||||
|
||||
echo
|
||||
echo '// Signals'
|
||||
echo 'const ('
|
||||
cat _error.out | grep -f _signal.grep | sed 's/=\(.*\)/= Signal(\1)/'
|
||||
echo ')'
|
||||
|
||||
# Run C program to print error and syscall strings.
|
||||
(
|
||||
echo -E "
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define nelem(x) (sizeof(x)/sizeof((x)[0]))
|
||||
|
||||
enum { A = 'A', Z = 'Z', a = 'a', z = 'z' }; // avoid need for single quotes below
|
||||
|
||||
int errors[] = {
|
||||
"
|
||||
for i in $errors
|
||||
do
|
||||
echo -E ' '$i,
|
||||
done
|
||||
|
||||
echo -E "
|
||||
};
|
||||
|
||||
int signals[] = {
|
||||
"
|
||||
for i in $signals
|
||||
do
|
||||
echo -E ' '$i,
|
||||
done
|
||||
|
||||
# Use -E because on some systems bash builtin interprets \n itself.
|
||||
echo -E '
|
||||
};
|
||||
|
||||
static int
|
||||
intcmp(const void *a, const void *b)
|
||||
{
|
||||
return *(int*)a - *(int*)b;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int i, j, e;
|
||||
char buf[1024], *p;
|
||||
|
||||
printf("\n\n// Error table\n");
|
||||
printf("var errors = [...]string {\n");
|
||||
qsort(errors, nelem(errors), sizeof errors[0], intcmp);
|
||||
for(i=0; i<nelem(errors); i++) {
|
||||
e = errors[i];
|
||||
if(i > 0 && errors[i-1] == e)
|
||||
continue;
|
||||
strcpy(buf, strerror(e));
|
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
|
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
|
||||
buf[0] += a - A;
|
||||
printf("\t%d: \"%s\",\n", e, buf);
|
||||
}
|
||||
printf("}\n\n");
|
||||
|
||||
printf("\n\n// Signal table\n");
|
||||
printf("var signals = [...]string {\n");
|
||||
qsort(signals, nelem(signals), sizeof signals[0], intcmp);
|
||||
for(i=0; i<nelem(signals); i++) {
|
||||
e = signals[i];
|
||||
if(i > 0 && signals[i-1] == e)
|
||||
continue;
|
||||
strcpy(buf, strsignal(e));
|
||||
// lowercase first letter: Bad -> bad, but STREAM -> STREAM.
|
||||
if(A <= buf[0] && buf[0] <= Z && a <= buf[1] && buf[1] <= z)
|
||||
buf[0] += a - A;
|
||||
// cut trailing : number.
|
||||
p = strrchr(buf, ":"[0]);
|
||||
if(p)
|
||||
*p = '\0';
|
||||
printf("\t%d: \"%s\",\n", e, buf);
|
||||
}
|
||||
printf("}\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
'
|
||||
) >_errors.c
|
||||
|
||||
$CC $ccflags -o _errors _errors.c && $GORUN ./_errors && rm -f _errors.c _errors _const.go _error.grep _signal.grep _error.out
|
||||
82
src/syscall/mkpost.go
Normal file
82
src/syscall/mkpost.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// mkpost processes the output of cgo -godefs to
|
||||
// modify the generated types. It is used to clean up
|
||||
// the syscall API in an architecture specific manner.
|
||||
//
|
||||
// mkpost is run after cgo -godefs by mkall.sh.
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
b, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
s := string(b)
|
||||
|
||||
goarch := os.Getenv("GOARCH")
|
||||
goos := os.Getenv("GOOS")
|
||||
switch {
|
||||
case 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, "_")
|
||||
|
||||
// We want to keep the X_ fields that are already consistently exported
|
||||
// for the other linux GOARCH settings.
|
||||
// Hide them and restore later.
|
||||
s = strings.Replace(s, "X__val", "MKPOSTFSIDVAL", 1)
|
||||
s = strings.Replace(s, "X__ifi_pad", "MKPOSTIFIPAD", 1)
|
||||
s = strings.Replace(s, "X_f", "MKPOSTSYSINFOTF", 1)
|
||||
|
||||
// Replace other unwanted fields with blank identifiers.
|
||||
re = regexp.MustCompile("X_[A-Za-z0-9_]*")
|
||||
s = re.ReplaceAllString(s, "_")
|
||||
|
||||
// Restore preserved fields.
|
||||
s = strings.Replace(s, "MKPOSTFSIDVAL", "X__val", 1)
|
||||
s = strings.Replace(s, "MKPOSTIFIPAD", "X__ifi_pad", 1)
|
||||
s = strings.Replace(s, "MKPOSTSYSINFOTF", "X_f", 1)
|
||||
|
||||
// Force the type of RawSockaddr.Data to [14]int8 to match
|
||||
// the existing gccgo API.
|
||||
re = regexp.MustCompile("(Data\\s+\\[14\\])uint8")
|
||||
s = re.ReplaceAllString(s, "${1}int8")
|
||||
|
||||
case goos == "freebsd":
|
||||
// Keep pre-FreeBSD 10 / non-POSIX 2008 names for timespec fields
|
||||
re := regexp.MustCompile("(A|M|C|Birth)tim\\s+Timespec")
|
||||
s = re.ReplaceAllString(s, "${1}timespec Timespec")
|
||||
}
|
||||
|
||||
// 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.*)")
|
||||
s = re.ReplaceAllString(string(b), "$1 | go run mkpost.go")
|
||||
|
||||
fmt.Print(s)
|
||||
}
|
||||
413
src/syscall/mksyscall.pl
Executable file
413
src/syscall/mksyscall.pl
Executable file
@@ -0,0 +1,413 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
|
||||
# This program reads a file containing function prototypes
|
||||
# (like syscall_darwin.go) and generates system call bodies.
|
||||
# The prototypes are marked by lines beginning with "//sys"
|
||||
# and read like func declarations if //sys is replaced by func, but:
|
||||
# * The parameter lists must give a name for each argument.
|
||||
# This includes return parameters.
|
||||
# * The parameter lists must give a type for each argument:
|
||||
# the (x, y, z int) shorthand is not allowed.
|
||||
# * If the return parameter is an error number, it must be named errno.
|
||||
|
||||
# A line beginning with //sysnb is like //sys, except that the
|
||||
# goroutine will not be suspended during the execution of the system
|
||||
# call. This must only be used for system calls which can never
|
||||
# block, as otherwise the system call could cause all goroutines to
|
||||
# hang.
|
||||
|
||||
use strict;
|
||||
|
||||
my $cmdline = "mksyscall.pl " . join(' ', @ARGV);
|
||||
my $errors = 0;
|
||||
my $_32bit = "";
|
||||
my $plan9 = 0;
|
||||
my $darwin = 0;
|
||||
my $openbsd = 0;
|
||||
my $netbsd = 0;
|
||||
my $dragonfly = 0;
|
||||
my $arm = 0; # 64-bit value should use (even, odd)-pair
|
||||
my $libc = 0;
|
||||
my $tags = ""; # build tags
|
||||
my $newtags = ""; # new style build tags
|
||||
my $stdimports = 'import "unsafe"';
|
||||
my $extraimports = "";
|
||||
|
||||
if($ARGV[0] eq "-b32") {
|
||||
$_32bit = "big-endian";
|
||||
shift;
|
||||
} elsif($ARGV[0] eq "-l32") {
|
||||
$_32bit = "little-endian";
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-plan9") {
|
||||
$plan9 = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-darwin") {
|
||||
$darwin = 1;
|
||||
$libc = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-openbsd") {
|
||||
$openbsd = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-netbsd") {
|
||||
$netbsd = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-dragonfly") {
|
||||
$dragonfly = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-arm") {
|
||||
$arm = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-libc") {
|
||||
$libc = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-tags") {
|
||||
shift;
|
||||
$tags = $ARGV[0];
|
||||
shift;
|
||||
}
|
||||
|
||||
if($ARGV[0] =~ /^-/) {
|
||||
print STDERR "usage: mksyscall.pl [-b32 | -l32] [-tags x,y] [file ...]\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if($libc) {
|
||||
$extraimports = 'import "internal/abi"';
|
||||
}
|
||||
if($darwin) {
|
||||
$extraimports .= "\nimport \"runtime\"";
|
||||
}
|
||||
|
||||
sub parseparamlist($) {
|
||||
my ($list) = @_;
|
||||
$list =~ s/^\s*//;
|
||||
$list =~ s/\s*$//;
|
||||
if($list eq "") {
|
||||
return ();
|
||||
}
|
||||
return split(/\s*,\s*/, $list);
|
||||
}
|
||||
|
||||
sub parseparam($) {
|
||||
my ($p) = @_;
|
||||
if($p !~ /^(\S*) (\S*)$/) {
|
||||
print STDERR "$ARGV:$.: malformed parameter: $p\n";
|
||||
$errors = 1;
|
||||
return ("xx", "int");
|
||||
}
|
||||
return ($1, $2);
|
||||
}
|
||||
|
||||
# set of trampolines we've already generated
|
||||
my %trampolines;
|
||||
|
||||
my $text = "";
|
||||
while(<>) {
|
||||
chomp;
|
||||
s/\s+/ /g;
|
||||
s/^\s+//;
|
||||
s/\s+$//;
|
||||
my $nonblock = /^\/\/sysnb /;
|
||||
next if !/^\/\/sys / && !$nonblock;
|
||||
|
||||
# Line must be of the form
|
||||
# func Open(path string, mode int, perm int) (fd int, errno error)
|
||||
# Split into name, in params, out params.
|
||||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*((?i)_?SYS_[A-Z0-9_]+))?$/) {
|
||||
print STDERR "$ARGV:$.: malformed //sys declaration\n";
|
||||
$errors = 1;
|
||||
next;
|
||||
}
|
||||
my ($func, $in, $out, $sysname) = ($2, $3, $4, $5);
|
||||
|
||||
# Split argument lists on comma.
|
||||
my @in = parseparamlist($in);
|
||||
my @out = parseparamlist($out);
|
||||
|
||||
# Try in vain to keep people from editing this file.
|
||||
# The theory is that they jump into the middle of the file
|
||||
# without reading the header.
|
||||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
|
||||
|
||||
if ((($darwin || ($openbsd && $libc)) && $func =~ /^ptrace(Ptr)?$/)) {
|
||||
# The ptrace function is called from forkAndExecInChild where stack
|
||||
# growth is forbidden.
|
||||
$text .= "//go:nosplit\n"
|
||||
}
|
||||
|
||||
# Go function header.
|
||||
my $out_decl = @out ? sprintf(" (%s)", join(', ', @out)) : "";
|
||||
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out_decl;
|
||||
|
||||
# Disable ptrace on iOS.
|
||||
if ($darwin && $func =~ /^ptrace(Ptr)?$/) {
|
||||
$text .= "\tif runtime.GOOS == \"ios\" {\n";
|
||||
$text .= "\t\tpanic(\"unimplemented\")\n";
|
||||
$text .= "\t}\n";
|
||||
}
|
||||
|
||||
# Check if err return available
|
||||
my $errvar = "";
|
||||
foreach my $p (@out) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type eq "error") {
|
||||
$errvar = $name;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Prepare arguments to Syscall.
|
||||
my @args = ();
|
||||
my $n = 0;
|
||||
foreach my $p (@in) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type =~ /^\*/) {
|
||||
push @args, "uintptr(unsafe.Pointer($name))";
|
||||
} elsif($type eq "string" && $errvar ne "") {
|
||||
$text .= "\tvar _p$n *byte\n";
|
||||
$text .= "\t_p$n, $errvar = BytePtrFromString($name)\n";
|
||||
$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type eq "string") {
|
||||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
|
||||
$text .= "\tvar _p$n *byte\n";
|
||||
$text .= "\t_p$n, _ = BytePtrFromString($name)\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type =~ /^\[\](.*)/) {
|
||||
# Convert slice into pointer, length.
|
||||
# Have to be careful not to take address of &a[0] if len == 0:
|
||||
# pass dummy pointer in that case.
|
||||
# Used to pass nil, but some OSes or simulators reject write(fd, nil, 0).
|
||||
$text .= "\tvar _p$n unsafe.Pointer\n";
|
||||
$text .= "\tif len($name) > 0 {\n\t\t_p$n = unsafe.Pointer(\&${name}[0])\n\t}";
|
||||
$text .= " else {\n\t\t_p$n = unsafe.Pointer(&_zero)\n\t}";
|
||||
$text .= "\n";
|
||||
push @args, "uintptr(_p$n)", "uintptr(len($name))";
|
||||
$n++;
|
||||
} elsif($type eq "int64" && ($openbsd || $netbsd)) {
|
||||
if (!$libc) {
|
||||
push @args, "0";
|
||||
}
|
||||
if($libc && $arm && @args % 2) {
|
||||
# arm abi specifies 64 bit argument must be 64 bit aligned.
|
||||
push @args, "0"
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name>>32)", "uintptr($name)";
|
||||
} elsif($_32bit eq "little-endian") {
|
||||
push @args, "uintptr($name)", "uintptr($name>>32)";
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
} elsif($type eq "int64" && $dragonfly) {
|
||||
if ($func !~ /^extp(read|write)/i) {
|
||||
push @args, "0";
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name>>32)", "uintptr($name)";
|
||||
} elsif($_32bit eq "little-endian") {
|
||||
push @args, "uintptr($name)", "uintptr($name>>32)";
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
} elsif($type eq "int64" && $_32bit ne "") {
|
||||
if(@args % 2 && $arm) {
|
||||
# arm abi specifies 64-bit argument uses
|
||||
# (even, odd) pair
|
||||
push @args, "0"
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name>>32)", "uintptr($name)";
|
||||
} else {
|
||||
push @args, "uintptr($name)", "uintptr($name>>32)";
|
||||
}
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
}
|
||||
|
||||
# Determine which form to use; pad args with zeros.
|
||||
my $asm = "Syscall";
|
||||
if ($nonblock) {
|
||||
if ($errvar eq "" && $ENV{'GOOS'} eq "linux") {
|
||||
$asm = "rawSyscallNoError";
|
||||
} else {
|
||||
$asm = "RawSyscall";
|
||||
}
|
||||
}
|
||||
if ($libc) {
|
||||
# Call unexported syscall functions (which take
|
||||
# libc functions instead of syscall numbers).
|
||||
$asm = lcfirst($asm);
|
||||
}
|
||||
if(@args <= 3) {
|
||||
while(@args < 3) {
|
||||
push @args, "0";
|
||||
}
|
||||
} elsif(@args <= 6) {
|
||||
$asm .= "6";
|
||||
while(@args < 6) {
|
||||
push @args, "0";
|
||||
}
|
||||
} elsif(@args <= 9) {
|
||||
$asm .= "9";
|
||||
while(@args < 9) {
|
||||
push @args, "0";
|
||||
}
|
||||
} else {
|
||||
print STDERR "$ARGV:$.: too many arguments to system call\n";
|
||||
}
|
||||
|
||||
if ($darwin || ($openbsd && $libc)) {
|
||||
# Use extended versions for calls that generate a 64-bit result.
|
||||
my ($name, $type) = parseparam($out[0]);
|
||||
if ($type eq "int64" || ($type eq "uintptr" && $_32bit eq "")) {
|
||||
$asm .= "X";
|
||||
}
|
||||
}
|
||||
|
||||
# System call number.
|
||||
my $funcname = "";
|
||||
if($sysname eq "") {
|
||||
$sysname = "SYS_$func";
|
||||
$sysname =~ s/([a-z])([A-Z])/${1}_$2/g; # turn FooBar into Foo_Bar
|
||||
$sysname =~ y/a-z/A-Z/;
|
||||
if($libc) {
|
||||
$sysname =~ y/A-Z/a-z/;
|
||||
$sysname = substr $sysname, 4;
|
||||
$funcname = "libc_$sysname";
|
||||
}
|
||||
}
|
||||
if($libc) {
|
||||
if($funcname eq "") {
|
||||
$sysname = substr $sysname, 4;
|
||||
$sysname =~ y/A-Z/a-z/;
|
||||
$funcname = "libc_$sysname";
|
||||
}
|
||||
$sysname = "abi.FuncPCABI0(${funcname}_trampoline)";
|
||||
}
|
||||
|
||||
# Actual call.
|
||||
my $args = join(', ', @args);
|
||||
my $call = "$asm($sysname, $args)";
|
||||
|
||||
# Assign return values.
|
||||
my $body = "";
|
||||
my @ret = ("_", "_", "_");
|
||||
my $do_errno = 0;
|
||||
for(my $i=0; $i<@out; $i++) {
|
||||
my $p = $out[$i];
|
||||
my ($name, $type) = parseparam($p);
|
||||
my $reg = "";
|
||||
if($name eq "err" && !$plan9) {
|
||||
$reg = "e1";
|
||||
$ret[2] = $reg;
|
||||
$do_errno = 1;
|
||||
} elsif($name eq "err" && $plan9) {
|
||||
$ret[0] = "r0";
|
||||
$ret[2] = "e1";
|
||||
next;
|
||||
} else {
|
||||
$reg = sprintf("r%d", $i);
|
||||
$ret[$i] = $reg;
|
||||
}
|
||||
if($type eq "bool") {
|
||||
$reg = "$reg != 0";
|
||||
}
|
||||
if($type eq "int64" && $_32bit ne "") {
|
||||
# 64-bit number in r1:r0 or r0:r1.
|
||||
if($i+2 > @out) {
|
||||
print STDERR "$ARGV:$.: not enough registers for int64 return\n";
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
|
||||
} else {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
|
||||
}
|
||||
$ret[$i] = sprintf("r%d", $i);
|
||||
$ret[$i+1] = sprintf("r%d", $i+1);
|
||||
}
|
||||
if($reg ne "e1" || $plan9) {
|
||||
$body .= "\t$name = $type($reg)\n";
|
||||
}
|
||||
}
|
||||
if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
|
||||
$text .= "\t$call\n";
|
||||
} else {
|
||||
if ($errvar eq "" && $ENV{'GOOS'} eq "linux") {
|
||||
# raw syscall without error on Linux, see golang.org/issue/22924
|
||||
$text .= "\t$ret[0], $ret[1] := $call\n";
|
||||
} else {
|
||||
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
|
||||
}
|
||||
}
|
||||
$text .= $body;
|
||||
|
||||
if ($plan9 && $ret[2] eq "e1") {
|
||||
$text .= "\tif int32(r0) == -1 {\n";
|
||||
$text .= "\t\terr = e1\n";
|
||||
$text .= "\t}\n";
|
||||
} elsif ($do_errno) {
|
||||
$text .= "\tif e1 != 0 {\n";
|
||||
$text .= "\t\terr = errnoErr(e1)\n";
|
||||
$text .= "\t}\n";
|
||||
}
|
||||
$text .= "\treturn\n";
|
||||
$text .= "}\n\n";
|
||||
if($libc) {
|
||||
if (not exists $trampolines{$funcname}) {
|
||||
$trampolines{$funcname} = 1;
|
||||
# The assembly trampoline that jumps to the libc routine.
|
||||
$text .= "func ${funcname}_trampoline()\n\n";
|
||||
# Tell the linker that funcname can be found in libSystem using varname without the libc_ prefix.
|
||||
my $basename = substr $funcname, 5;
|
||||
my $libc = "libc.so";
|
||||
if ($darwin) {
|
||||
$libc = "/usr/lib/libSystem.B.dylib";
|
||||
}
|
||||
$text .= "//go:cgo_import_dynamic $funcname $basename \"$libc\"\n\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
chomp $text;
|
||||
chomp $text;
|
||||
|
||||
if($errors) {
|
||||
exit 1;
|
||||
}
|
||||
|
||||
if($extraimports ne "") {
|
||||
$stdimports .= "\n$extraimports";
|
||||
}
|
||||
|
||||
# TODO: this assumes tags are just simply comma separated. For now this is all the uses.
|
||||
$newtags = $tags =~ s/,/ && /r;
|
||||
|
||||
print <<EOF;
|
||||
// $cmdline
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
//go:build $newtags
|
||||
|
||||
package syscall
|
||||
|
||||
$stdimports
|
||||
|
||||
$text
|
||||
EOF
|
||||
exit 0;
|
||||
338
src/syscall/mksyscall_libc.pl
Executable file
338
src/syscall/mksyscall_libc.pl
Executable file
@@ -0,0 +1,338 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
|
||||
# This program reads a file containing function prototypes
|
||||
# (like syscall_solaris.go) and generates system call bodies.
|
||||
# The prototypes are marked by lines beginning with "//sys"
|
||||
# and read like func declarations if //sys is replaced by func, but:
|
||||
# * The parameter lists must give a name for each argument.
|
||||
# This includes return parameters.
|
||||
# * The parameter lists must give a type for each argument:
|
||||
# the (x, y, z int) shorthand is not allowed.
|
||||
# * If the return parameter is an error number, it must be named err.
|
||||
# * If go func name needs to be different than its libc name,
|
||||
# * or the function is not in libc, name could be specified
|
||||
# * at the end, after "=" sign, like
|
||||
# //sys getsockopt(s int, level int, name int, val uintptr, vallen *_Socklen) (err error) = libsocket.getsockopt
|
||||
|
||||
use strict;
|
||||
|
||||
my $cmdline = "mksyscall_libc.pl " . join(' ', @ARGV);
|
||||
my $errors = 0;
|
||||
my $_32bit = "";
|
||||
my $tags = ""; # build tags
|
||||
my $newtags = ""; # new style build tags
|
||||
my $aix = 0;
|
||||
my $solaris = 0;
|
||||
|
||||
binmode STDOUT;
|
||||
|
||||
if($ARGV[0] eq "-b32") {
|
||||
$_32bit = "big-endian";
|
||||
shift;
|
||||
} elsif($ARGV[0] eq "-l32") {
|
||||
$_32bit = "little-endian";
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-aix") {
|
||||
$aix = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-solaris") {
|
||||
$solaris = 1;
|
||||
shift;
|
||||
}
|
||||
if($ARGV[0] eq "-tags") {
|
||||
shift;
|
||||
$tags = $ARGV[0];
|
||||
shift;
|
||||
}
|
||||
|
||||
|
||||
if($ARGV[0] =~ /^-/) {
|
||||
print STDERR "usage: mksyscall_libc.pl [-b32 | -l32] [-aix | -solaris] [-tags x,y] [file ...]\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
sub parseparamlist($) {
|
||||
my ($list) = @_;
|
||||
$list =~ s/^\s*//;
|
||||
$list =~ s/\s*$//;
|
||||
if($list eq "") {
|
||||
return ();
|
||||
}
|
||||
return split(/\s*,\s*/, $list);
|
||||
}
|
||||
|
||||
sub parseparam($) {
|
||||
my ($p) = @_;
|
||||
if($p !~ /^(\S*) (\S*)$/) {
|
||||
print STDERR "$ARGV:$.: malformed parameter: $p\n";
|
||||
$errors = 1;
|
||||
return ("xx", "int");
|
||||
}
|
||||
return ($1, $2);
|
||||
}
|
||||
|
||||
my $package = "";
|
||||
my $text = "";
|
||||
my $dynimports = "";
|
||||
my $linknames = "";
|
||||
my @vars = ();
|
||||
while(<>) {
|
||||
chomp;
|
||||
s/\s+/ /g;
|
||||
s/^\s+//;
|
||||
s/\s+$//;
|
||||
$package = $1 if !$package && /^package (\S+)$/;
|
||||
my $nonblock = /^\/\/sysnb /;
|
||||
next if !/^\/\/sys / && !$nonblock;
|
||||
|
||||
my $syscalldot = "";
|
||||
$syscalldot = "syscall." if $package ne "syscall";
|
||||
|
||||
# Line must be of the form
|
||||
# func Open(path string, mode int, perm int) (fd int, err error)
|
||||
# Split into name, in params, out params.
|
||||
if(!/^\/\/sys(nb)? (\w+)\(([^()]*)\)\s*(?:\(([^()]+)\))?\s*(?:=\s*(?:(\w*)\.)?(\w*))?$/) {
|
||||
print STDERR "$ARGV:$.: malformed //sys declaration\n";
|
||||
$errors = 1;
|
||||
next;
|
||||
}
|
||||
my ($nb, $func, $in, $out, $modname, $sysname) = ($1, $2, $3, $4, $5, $6);
|
||||
|
||||
# Split argument lists on comma.
|
||||
my @in = parseparamlist($in);
|
||||
my @out = parseparamlist($out);
|
||||
|
||||
# Try in vain to keep people from editing this file.
|
||||
# The theory is that they jump into the middle of the file
|
||||
# without reading the header.
|
||||
$text .= "// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT\n\n";
|
||||
|
||||
# So file name.
|
||||
if($aix) {
|
||||
if($modname eq "") {
|
||||
$modname = "libc.a/shr_64.o";
|
||||
} else {
|
||||
print STDERR "$func: only syscall using libc are available\n";
|
||||
$errors = 1;
|
||||
next;
|
||||
}
|
||||
|
||||
}
|
||||
if($solaris) {
|
||||
if($modname eq "") {
|
||||
$modname = "libc";
|
||||
}
|
||||
$modname .= ".so";
|
||||
|
||||
}
|
||||
|
||||
# System call name.
|
||||
if($sysname eq "") {
|
||||
$sysname = "$func";
|
||||
}
|
||||
|
||||
# System call pointer variable name.
|
||||
my $sysvarname = "libc_${sysname}";
|
||||
|
||||
my $strconvfunc = "BytePtrFromString";
|
||||
my $strconvtype = "*byte";
|
||||
|
||||
$sysname =~ y/A-Z/a-z/; # All libc functions are lowercase.
|
||||
|
||||
# Runtime import of function to allow cross-platform builds.
|
||||
if($dynimports !~ /\s+${sysvarname}\s+/) {
|
||||
$dynimports .= "//go:cgo_import_dynamic ${sysvarname} ${sysname} \"$modname\"\n";
|
||||
# Link symbol to proc address variable.
|
||||
$linknames .= "//go:linkname ${sysvarname} ${sysvarname}\n";
|
||||
# Library proc address variable.
|
||||
push @vars, $sysvarname;
|
||||
}
|
||||
|
||||
# Go function header.
|
||||
$out = join(', ', @out);
|
||||
if($out ne "") {
|
||||
$out = " ($out)";
|
||||
}
|
||||
if($text ne "") {
|
||||
$text .= "\n"
|
||||
}
|
||||
$text .= sprintf "func %s(%s)%s {\n", $func, join(', ', @in), $out;
|
||||
|
||||
# Check if err return available
|
||||
my $errvar = "";
|
||||
foreach my $p (@out) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type eq "error") {
|
||||
$errvar = $name;
|
||||
last;
|
||||
}
|
||||
}
|
||||
|
||||
# Prepare arguments to Syscall.
|
||||
my @args = ();
|
||||
my $n = 0;
|
||||
foreach my $p (@in) {
|
||||
my ($name, $type) = parseparam($p);
|
||||
if($type =~ /^\*/) {
|
||||
push @args, "uintptr(unsafe.Pointer($name))";
|
||||
} elsif($type eq "string" && $errvar ne "") {
|
||||
$text .= "\tvar _p$n $strconvtype\n";
|
||||
$text .= "\t_p$n, $errvar = $strconvfunc($name)\n";
|
||||
$text .= "\tif $errvar != nil {\n\t\treturn\n\t}\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type eq "string") {
|
||||
print STDERR "$ARGV:$.: $func uses string arguments, but has no error return\n";
|
||||
$text .= "\tvar _p$n $strconvtype\n";
|
||||
$text .= "\t_p$n, _ = $strconvfunc($name)\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))";
|
||||
$n++;
|
||||
} elsif($type =~ /^\[\](.*)/) {
|
||||
# Convert slice into pointer, length.
|
||||
# Have to be careful not to take address of &a[0] if len == 0:
|
||||
# pass nil in that case.
|
||||
$text .= "\tvar _p$n *$1\n";
|
||||
$text .= "\tif len($name) > 0 {\n\t\t_p$n = \&$name\[0]\n\t}\n";
|
||||
push @args, "uintptr(unsafe.Pointer(_p$n))", "uintptr(len($name))";
|
||||
$n++;
|
||||
} elsif($type eq "int64" && $_32bit ne "") {
|
||||
if($_32bit eq "big-endian") {
|
||||
push @args, "uintptr($name >> 32)", "uintptr($name)";
|
||||
} else {
|
||||
push @args, "uintptr($name)", "uintptr($name >> 32)";
|
||||
}
|
||||
} elsif($type eq "bool") {
|
||||
$text .= "\tvar _p$n uint32\n";
|
||||
$text .= "\tif $name {\n\t\t_p$n = 1\n\t} else {\n\t\t_p$n = 0\n\t}\n";
|
||||
push @args, "uintptr(_p$n)";
|
||||
$n++;
|
||||
} else {
|
||||
push @args, "uintptr($name)";
|
||||
}
|
||||
}
|
||||
my $nargs = @args;
|
||||
|
||||
my $asmfuncname="";
|
||||
my $asmrawfuncname="";
|
||||
|
||||
if($aix){
|
||||
$asmfuncname="syscall6";
|
||||
$asmrawfuncname="rawSyscall6";
|
||||
} else {
|
||||
$asmfuncname="sysvicall6";
|
||||
$asmrawfuncname="rawSysvicall6";
|
||||
}
|
||||
|
||||
# Determine which form to use; pad args with zeros.
|
||||
my $asm = "${syscalldot}${asmfuncname}";
|
||||
if ($nonblock) {
|
||||
$asm = "${syscalldot}${asmrawfuncname}";
|
||||
}
|
||||
if(@args <= 6) {
|
||||
while(@args < 6) {
|
||||
push @args, "0";
|
||||
}
|
||||
} else {
|
||||
print STDERR "$ARGV:$.: too many arguments to system call\n";
|
||||
}
|
||||
|
||||
# Actual call.
|
||||
my $args = join(', ', @args);
|
||||
my $call = "$asm(uintptr(unsafe.Pointer(&$sysvarname)), $nargs, $args)";
|
||||
|
||||
# Assign return values.
|
||||
my $body = "";
|
||||
my $failexpr = "";
|
||||
my @ret = ("_", "_", "_");
|
||||
my @pout= ();
|
||||
my $do_errno = 0;
|
||||
for(my $i=0; $i<@out; $i++) {
|
||||
my $p = $out[$i];
|
||||
my ($name, $type) = parseparam($p);
|
||||
my $reg = "";
|
||||
if($name eq "err") {
|
||||
$reg = "e1";
|
||||
$ret[2] = $reg;
|
||||
$do_errno = 1;
|
||||
} else {
|
||||
$reg = sprintf("r%d", $i);
|
||||
$ret[$i] = $reg;
|
||||
}
|
||||
if($type eq "bool") {
|
||||
$reg = "$reg != 0";
|
||||
}
|
||||
if($type eq "int64" && $_32bit ne "") {
|
||||
# 64-bit number in r1:r0 or r0:r1.
|
||||
if($i+2 > @out) {
|
||||
print STDERR "$ARGV:$.: not enough registers for int64 return\n";
|
||||
}
|
||||
if($_32bit eq "big-endian") {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i, $i+1);
|
||||
} else {
|
||||
$reg = sprintf("int64(r%d)<<32 | int64(r%d)", $i+1, $i);
|
||||
}
|
||||
$ret[$i] = sprintf("r%d", $i);
|
||||
$ret[$i+1] = sprintf("r%d", $i+1);
|
||||
}
|
||||
if($reg ne "e1") {
|
||||
$body .= "\t$name = $type($reg)\n";
|
||||
}
|
||||
}
|
||||
if ($ret[0] eq "_" && $ret[1] eq "_" && $ret[2] eq "_") {
|
||||
$text .= "\t$call\n";
|
||||
} else {
|
||||
$text .= "\t$ret[0], $ret[1], $ret[2] := $call\n";
|
||||
}
|
||||
$text .= $body;
|
||||
|
||||
if ($do_errno) {
|
||||
$text .= "\tif e1 != 0 {\n";
|
||||
$text .= "\t\terr = errnoErr(e1)\n";
|
||||
$text .= "\t}\n";
|
||||
}
|
||||
$text .= "\treturn\n";
|
||||
$text .= "}\n";
|
||||
}
|
||||
|
||||
if($errors) {
|
||||
exit 1;
|
||||
}
|
||||
|
||||
# TODO: this assumes tags are just simply comma separated. For now this is all the uses.
|
||||
$newtags = $tags =~ s/,/ && /r;
|
||||
|
||||
print <<EOF;
|
||||
// $cmdline
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
//go:build $newtags
|
||||
|
||||
package $package
|
||||
|
||||
import "unsafe"
|
||||
EOF
|
||||
|
||||
print "import \"syscall\"\n" if $package ne "syscall";
|
||||
|
||||
my $vardecls = "\t" . join(",\n\t", @vars);
|
||||
$vardecls .= " libcFunc";
|
||||
|
||||
chomp($_=<<EOF);
|
||||
|
||||
$dynimports
|
||||
$linknames
|
||||
type libcFunc uintptr
|
||||
|
||||
var (
|
||||
$vardecls
|
||||
)
|
||||
|
||||
$text
|
||||
EOF
|
||||
print $_;
|
||||
exit 0;
|
||||
60
src/syscall/mksyscall_windows.go
Normal file
60
src/syscall/mksyscall_windows.go
Normal file
@@ -0,0 +1,60 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build ignore
|
||||
|
||||
// mksyscall_windows wraps golang.org/x/sys/windows/mkwinsyscall.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func main() {
|
||||
goTool := filepath.Join(runtime.GOROOT(), "bin", "go")
|
||||
|
||||
listCmd := exec.Command(goTool, "list", "-m")
|
||||
listCmd.Env = append(os.Environ(), "GO111MODULE=on")
|
||||
|
||||
var (
|
||||
cmdEnv []string
|
||||
modArgs []string
|
||||
)
|
||||
if out, err := listCmd.Output(); err == nil && string(bytes.TrimSpace(out)) == "std" {
|
||||
// Force module mode to use mkwinsyscall at the same version as the x/sys
|
||||
// module vendored into the standard library.
|
||||
cmdEnv = append(os.Environ(), "GO111MODULE=on")
|
||||
|
||||
// Force -mod=readonly instead of the default -mod=vendor.
|
||||
//
|
||||
// mkwinsyscall is not itself vendored into the standard library, and it is
|
||||
// not feasible to do so at the moment: std-vendored libraries are included
|
||||
// in the "std" meta-pattern (because in general they *are* linked into
|
||||
// users binaries separately from the original import paths), and we can't
|
||||
// allow a binary in the "std" meta-pattern.
|
||||
modArgs = []string{"-mod=readonly"}
|
||||
} else {
|
||||
// Nobody outside the standard library should be using this wrapper: other
|
||||
// modules can vendor in the mkwinsyscall tool directly (as described in
|
||||
// https://golang.org/issue/25922), so they don't need this wrapper to
|
||||
// set module mode and -mod=readonly explicitly.
|
||||
os.Stderr.WriteString("WARNING: Please switch from using:\n go run $GOROOT/src/syscall/mksyscall_windows.go\nto using:\n go run golang.org/x/sys/windows/mkwinsyscall\n")
|
||||
}
|
||||
|
||||
args := append([]string{"run"}, modArgs...)
|
||||
args = append(args, "golang.org/x/sys/windows/mkwinsyscall")
|
||||
args = append(args, os.Args[1:]...)
|
||||
cmd := exec.Command(goTool, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
cmd.Env = cmdEnv
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
257
src/syscall/mksysctl_openbsd.pl
Executable file
257
src/syscall/mksysctl_openbsd.pl
Executable file
@@ -0,0 +1,257 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
# 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.
|
||||
|
||||
#
|
||||
# Parse the header files for OpenBSD and generate a Go usable sysctl MIB.
|
||||
#
|
||||
# Build a MIB with each entry being an array containing the level, type and
|
||||
# a hash that will contain additional entries if the current entry is a node.
|
||||
# We then walk this MIB and create a flattened sysctl name to OID hash.
|
||||
#
|
||||
|
||||
use strict;
|
||||
|
||||
my $debug = 0;
|
||||
my %ctls = ();
|
||||
|
||||
my @headers = qw (
|
||||
sys/sysctl.h
|
||||
sys/socket.h
|
||||
sys/tty.h
|
||||
sys/malloc.h
|
||||
sys/mount.h
|
||||
sys/namei.h
|
||||
sys/sem.h
|
||||
sys/shm.h
|
||||
sys/vmmeter.h
|
||||
uvm/uvm_param.h
|
||||
uvm/uvm_swap_encrypt.h
|
||||
ddb/db_var.h
|
||||
net/if.h
|
||||
net/if_pfsync.h
|
||||
net/pipex.h
|
||||
netinet/in.h
|
||||
netinet/icmp_var.h
|
||||
netinet/igmp_var.h
|
||||
netinet/ip_ah.h
|
||||
netinet/ip_carp.h
|
||||
netinet/ip_divert.h
|
||||
netinet/ip_esp.h
|
||||
netinet/ip_ether.h
|
||||
netinet/ip_gre.h
|
||||
netinet/ip_ipcomp.h
|
||||
netinet/ip_ipip.h
|
||||
netinet/pim_var.h
|
||||
netinet/tcp_var.h
|
||||
netinet/udp_var.h
|
||||
netinet6/in6.h
|
||||
netinet6/ip6_divert.h
|
||||
netinet6/pim6_var.h
|
||||
netinet/icmp6.h
|
||||
netmpls/mpls.h
|
||||
);
|
||||
|
||||
my @ctls = qw (
|
||||
kern
|
||||
vm
|
||||
fs
|
||||
net
|
||||
#debug # Special handling required
|
||||
hw
|
||||
#machdep # Arch specific
|
||||
user
|
||||
ddb
|
||||
#vfs # Special handling required
|
||||
fs.posix
|
||||
kern.forkstat
|
||||
kern.intrcnt
|
||||
kern.malloc
|
||||
kern.nchstats
|
||||
kern.seminfo
|
||||
kern.shminfo
|
||||
kern.timecounter
|
||||
kern.tty
|
||||
kern.watchdog
|
||||
net.bpf
|
||||
net.ifq
|
||||
net.inet
|
||||
net.inet.ah
|
||||
net.inet.carp
|
||||
net.inet.divert
|
||||
net.inet.esp
|
||||
net.inet.etherip
|
||||
net.inet.gre
|
||||
net.inet.icmp
|
||||
net.inet.igmp
|
||||
net.inet.ip
|
||||
net.inet.ip.ifq
|
||||
net.inet.ipcomp
|
||||
net.inet.ipip
|
||||
net.inet.mobileip
|
||||
net.inet.pfsync
|
||||
net.inet.pim
|
||||
net.inet.tcp
|
||||
net.inet.udp
|
||||
net.inet6
|
||||
net.inet6.divert
|
||||
net.inet6.ip6
|
||||
net.inet6.icmp6
|
||||
net.inet6.pim6
|
||||
net.inet6.tcp6
|
||||
net.inet6.udp6
|
||||
net.mpls
|
||||
net.mpls.ifq
|
||||
net.key
|
||||
net.pflow
|
||||
net.pfsync
|
||||
net.pipex
|
||||
net.rt
|
||||
vm.swapencrypt
|
||||
#vfsgenctl # Special handling required
|
||||
);
|
||||
|
||||
# Node name "fixups"
|
||||
my %ctl_map = (
|
||||
"ipproto" => "net.inet",
|
||||
"net.inet.ipproto" => "net.inet",
|
||||
"net.inet6.ipv6proto" => "net.inet6",
|
||||
"net.inet6.ipv6" => "net.inet6.ip6",
|
||||
"net.inet.icmpv6" => "net.inet6.icmp6",
|
||||
"net.inet6.divert6" => "net.inet6.divert",
|
||||
"net.inet6.tcp6" => "net.inet.tcp",
|
||||
"net.inet6.udp6" => "net.inet.udp",
|
||||
"mpls" => "net.mpls",
|
||||
"swpenc" => "vm.swapencrypt"
|
||||
);
|
||||
|
||||
# Node mappings
|
||||
my %node_map = (
|
||||
"net.inet.ip.ifq" => "net.ifq",
|
||||
"net.inet.pfsync" => "net.pfsync",
|
||||
"net.mpls.ifq" => "net.ifq"
|
||||
);
|
||||
|
||||
my $ctlname;
|
||||
my %mib = ();
|
||||
my %sysctl = ();
|
||||
my $node;
|
||||
|
||||
sub debug() {
|
||||
print STDERR "$_[0]\n" if $debug;
|
||||
}
|
||||
|
||||
# Walk the MIB and build a sysctl name to OID mapping.
|
||||
sub build_sysctl() {
|
||||
my ($node, $name, $oid) = @_;
|
||||
my %node = %{$node};
|
||||
my @oid = @{$oid};
|
||||
|
||||
foreach my $key (sort keys %node) {
|
||||
my @node = @{$node{$key}};
|
||||
my $nodename = $name.($name ne '' ? '.' : '').$key;
|
||||
my @nodeoid = (@oid, $node[0]);
|
||||
if ($node[1] eq 'CTLTYPE_NODE') {
|
||||
if (exists $node_map{$nodename}) {
|
||||
$node = \%mib;
|
||||
$ctlname = $node_map{$nodename};
|
||||
foreach my $part (split /\./, $ctlname) {
|
||||
$node = \%{@{$$node{$part}}[2]};
|
||||
}
|
||||
} else {
|
||||
$node = $node[2];
|
||||
}
|
||||
&build_sysctl($node, $nodename, \@nodeoid);
|
||||
} elsif ($node[1] ne '') {
|
||||
$sysctl{$nodename} = \@nodeoid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $ctl (@ctls) {
|
||||
$ctls{$ctl} = $ctl;
|
||||
}
|
||||
|
||||
# Build MIB
|
||||
foreach my $header (@headers) {
|
||||
&debug("Processing $header...");
|
||||
open HEADER, "/usr/include/$header" ||
|
||||
print STDERR "Failed to open $header\n";
|
||||
while (<HEADER>) {
|
||||
if ($_ =~ /^#define\s+(CTL_NAMES)\s+{/ ||
|
||||
$_ =~ /^#define\s+(CTL_(.*)_NAMES)\s+{/ ||
|
||||
$_ =~ /^#define\s+((.*)CTL_NAMES)\s+{/) {
|
||||
if ($1 eq 'CTL_NAMES') {
|
||||
# Top level.
|
||||
$node = \%mib;
|
||||
} else {
|
||||
# Node.
|
||||
my $nodename = lc($2);
|
||||
if ($header =~ /^netinet\//) {
|
||||
$ctlname = "net.inet.$nodename";
|
||||
} elsif ($header =~ /^netinet6\//) {
|
||||
$ctlname = "net.inet6.$nodename";
|
||||
} elsif ($header =~ /^net\//) {
|
||||
$ctlname = "net.$nodename";
|
||||
} else {
|
||||
$ctlname = "$nodename";
|
||||
$ctlname =~ s/^(fs|net|kern)_/$1\./;
|
||||
}
|
||||
if (exists $ctl_map{$ctlname}) {
|
||||
$ctlname = $ctl_map{$ctlname};
|
||||
}
|
||||
if (not exists $ctls{$ctlname}) {
|
||||
&debug("Ignoring $ctlname...");
|
||||
next;
|
||||
}
|
||||
|
||||
# Walk down from the top of the MIB.
|
||||
$node = \%mib;
|
||||
foreach my $part (split /\./, $ctlname) {
|
||||
if (not exists $$node{$part}) {
|
||||
&debug("Missing node $part");
|
||||
$$node{$part} = [ 0, '', {} ];
|
||||
}
|
||||
$node = \%{@{$$node{$part}}[2]};
|
||||
}
|
||||
}
|
||||
|
||||
# Populate current node with entries.
|
||||
my $i = -1;
|
||||
while (defined($_) && $_ !~ /^}/) {
|
||||
$_ = <HEADER>;
|
||||
$i++ if $_ =~ /{.*}/;
|
||||
next if $_ !~ /{\s+"(\w+)",\s+(CTLTYPE_[A-Z]+)\s+}/;
|
||||
$$node{$1} = [ $i, $2, {} ];
|
||||
}
|
||||
}
|
||||
}
|
||||
close HEADER;
|
||||
}
|
||||
|
||||
&build_sysctl(\%mib, "", []);
|
||||
|
||||
print <<EOF;
|
||||
// mksysctl_openbsd.pl
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall;
|
||||
|
||||
type mibentry struct {
|
||||
ctlname string
|
||||
ctloid []_C_int
|
||||
}
|
||||
|
||||
var sysctlMib = []mibentry {
|
||||
EOF
|
||||
|
||||
foreach my $name (sort keys %sysctl) {
|
||||
my @oid = @{$sysctl{$name}};
|
||||
print "\t{ \"$name\", []_C_int{ ", join(', ', @oid), " } }, \n";
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
}
|
||||
EOF
|
||||
43
src/syscall/mksysnum_dragonfly.pl
Executable file
43
src/syscall/mksysnum_dragonfly.pl
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
#
|
||||
# Generate system call table for DragonFly from master list
|
||||
# (for example, /usr/src/sys/kern/syscalls.master).
|
||||
|
||||
use strict;
|
||||
|
||||
my $command = "mksysnum_dragonfly.pl " . join(' ', @ARGV);
|
||||
|
||||
print <<EOF;
|
||||
// $command
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall
|
||||
|
||||
const (
|
||||
EOF
|
||||
|
||||
while(<>){
|
||||
if(/^([0-9]+)\s+STD\s+({ \S+\s+(\w+).*)$/){
|
||||
my $num = $1;
|
||||
my $proto = $2;
|
||||
my $name = "SYS_$3";
|
||||
$name =~ y/a-z/A-Z/;
|
||||
|
||||
# There are multiple entries for enosys and nosys, so comment them out.
|
||||
if($name =~ /^SYS_E?NOSYS$/){
|
||||
$name = "// $name";
|
||||
}
|
||||
if($name eq 'SYS_SYS_EXIT'){
|
||||
$name = 'SYS_EXIT';
|
||||
}
|
||||
|
||||
print " $name = $num; // $proto\n";
|
||||
}
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
)
|
||||
EOF
|
||||
56
src/syscall/mksysnum_freebsd.pl
Executable file
56
src/syscall/mksysnum_freebsd.pl
Executable file
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
#
|
||||
# Generate system call table for FreeBSD from master list
|
||||
# (for example, /usr/src/sys/kern/syscalls.master).
|
||||
|
||||
use strict;
|
||||
|
||||
my $command = "mksysnum_freebsd.pl " . join(' ', @ARGV);
|
||||
|
||||
print <<EOF;
|
||||
// $command
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall
|
||||
|
||||
const (
|
||||
EOF
|
||||
|
||||
while(<>){
|
||||
if(/^([0-9]+)\s+\S+\s+STD\s+({ \S+\s+(\w+).*)$/){
|
||||
my $num = $1;
|
||||
my $proto = $2;
|
||||
my $name = "SYS_$3";
|
||||
$name =~ y/a-z/A-Z/;
|
||||
|
||||
# There are multiple entries for enosys and nosys, so comment them out.
|
||||
if($name =~ /^SYS_E?NOSYS$/){
|
||||
$name = "// $name";
|
||||
}
|
||||
if($name eq 'SYS_SYS_EXIT'){
|
||||
$name = 'SYS_EXIT';
|
||||
}
|
||||
if($name =~ /^SYS_CAP_+/ || $name =~ /^SYS___CAP_+/){
|
||||
next
|
||||
}
|
||||
|
||||
print " $name = $num; // $proto\n";
|
||||
|
||||
# We keep Capsicum syscall numbers for FreeBSD
|
||||
# 9-STABLE here because we are not sure whether they
|
||||
# are mature and stable.
|
||||
if($num == 513){
|
||||
print " SYS_CAP_NEW = 514 // { int cap_new(int fd, uint64_t rights); }\n";
|
||||
print " SYS_CAP_GETRIGHTS = 515 // { int cap_getrights(int fd, \\\n";
|
||||
print " SYS_CAP_ENTER = 516 // { int cap_enter(void); }\n";
|
||||
print " SYS_CAP_GETMODE = 517 // { int cap_getmode(u_int *modep); }\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
)
|
||||
EOF
|
||||
61
src/syscall/mksysnum_linux.pl
Executable file
61
src/syscall/mksysnum_linux.pl
Executable file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
|
||||
use strict;
|
||||
|
||||
my $command = "mksysnum_linux.pl ". join(' ', @ARGV);
|
||||
|
||||
print <<EOF;
|
||||
// $command
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall
|
||||
|
||||
const(
|
||||
EOF
|
||||
|
||||
my $offset = 0;
|
||||
|
||||
sub fmt {
|
||||
my ($name, $num) = @_;
|
||||
if($num > 999){
|
||||
# ignore deprecated syscalls that are no longer implemented
|
||||
# https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/uapi/asm-generic/unistd.h?id=refs/heads/master#n716
|
||||
return;
|
||||
}
|
||||
$name =~ y/a-z/A-Z/;
|
||||
$num = $num + $offset;
|
||||
print " SYS_$name = $num;\n";
|
||||
}
|
||||
|
||||
my $prev;
|
||||
open(GCC, "gcc -E -dD $ARGV[0] |") || die "can't run gcc";
|
||||
while(<GCC>){
|
||||
if(/^#define __NR_Linux\s+([0-9]+)/){
|
||||
# mips/mips64: extract offset
|
||||
$offset = $1;
|
||||
}
|
||||
elsif(/^#define __NR_syscalls\s+/) {
|
||||
# ignore redefinitions of __NR_syscalls
|
||||
}
|
||||
elsif(/^#define __NR_(\w+)\s+([0-9]+)/){
|
||||
$prev = $2;
|
||||
fmt($1, $2);
|
||||
}
|
||||
elsif(/^#define __NR3264_(\w+)\s+([0-9]+)/){
|
||||
$prev = $2;
|
||||
fmt($1, $2);
|
||||
}
|
||||
elsif(/^#define __NR_(\w+)\s+\(\w+\+\s*([0-9]+)\)/){
|
||||
fmt($1, $prev+$2)
|
||||
}
|
||||
elsif(/^#define __NR_(\w+)\s+\(__NR_Linux \+ ([0-9]+)/){
|
||||
fmt($1, $2);
|
||||
}
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
)
|
||||
EOF
|
||||
51
src/syscall/mksysnum_netbsd.pl
Executable file
51
src/syscall/mksysnum_netbsd.pl
Executable file
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
#
|
||||
# Generate system call table for OpenBSD from master list
|
||||
# (for example, /usr/src/sys/kern/syscalls.master).
|
||||
|
||||
use strict;
|
||||
|
||||
my $command = "mksysnum_netbsd.pl " . join(' ', @ARGV);
|
||||
|
||||
print <<EOF;
|
||||
// $command
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall
|
||||
|
||||
const (
|
||||
EOF
|
||||
|
||||
my $line = '';
|
||||
while(<>){
|
||||
if($line =~ /^(.*)\\$/) {
|
||||
# Handle continuation
|
||||
$line = $1;
|
||||
$_ =~ s/^\s+//;
|
||||
$line .= $_;
|
||||
} else {
|
||||
# New line
|
||||
$line = $_;
|
||||
}
|
||||
next if $line =~ /\\$/;
|
||||
if($line =~ /^([0-9]+)\s+((STD)|(NOERR))\s+(RUMP\s+)?({\s+\S+\s*\*?\s*\|(\S+)\|(\S*)\|(\w+).*\s+})(\s+(\S+))?$/) {
|
||||
my $num = $1;
|
||||
my $proto = $6;
|
||||
my $compat = $8;
|
||||
my $name = "$7_$9";
|
||||
|
||||
$name = "$7_$11" if $11 ne '';
|
||||
$name =~ y/a-z/A-Z/;
|
||||
|
||||
if($compat eq '' || $compat eq '30' || $compat eq '50') {
|
||||
print " $name = $num; // $proto\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
)
|
||||
EOF
|
||||
43
src/syscall/mksysnum_openbsd.pl
Executable file
43
src/syscall/mksysnum_openbsd.pl
Executable file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env perl
|
||||
# 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.
|
||||
#
|
||||
# Generate system call table for OpenBSD from master list
|
||||
# (for example, /usr/src/sys/kern/syscalls.master).
|
||||
|
||||
use strict;
|
||||
|
||||
my $command = "mksysnum_openbsd.pl " . join(' ', @ARGV);
|
||||
|
||||
print <<EOF;
|
||||
// $command
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall
|
||||
|
||||
const (
|
||||
EOF
|
||||
|
||||
while(<>){
|
||||
if(/^([0-9]+)\s+STD\s+(NOLOCK\s+)?(\{ \S+\s+\*?(\w+).*)$/){
|
||||
my $num = $1;
|
||||
my $proto = $3;
|
||||
my $name = $4;
|
||||
$name =~ y/a-z/A-Z/;
|
||||
|
||||
# There are multiple entries for enosys and nosys, so comment them out.
|
||||
if($name =~ /^SYS_E?NOSYS$/){
|
||||
$name = "// $name";
|
||||
}
|
||||
if($name eq 'SYS_SYS_EXIT'){
|
||||
$name = 'SYS_EXIT';
|
||||
}
|
||||
|
||||
print " $name = $num; // $proto\n";
|
||||
}
|
||||
}
|
||||
|
||||
print <<EOF;
|
||||
)
|
||||
EOF
|
||||
23
src/syscall/mksysnum_plan9.sh
Executable file
23
src/syscall/mksysnum_plan9.sh
Executable file
@@ -0,0 +1,23 @@
|
||||
#!/bin/sh
|
||||
# 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.
|
||||
|
||||
COMMAND="mksysnum_plan9.sh $@"
|
||||
|
||||
cat <<EOF
|
||||
// $COMMAND
|
||||
// Code generated by the command above; DO NOT EDIT.
|
||||
|
||||
package syscall
|
||||
|
||||
const(
|
||||
EOF
|
||||
|
||||
SP='[ ]' # space or tab
|
||||
sed "s/^#define${SP}\\([A-Z0-9_][A-Z0-9_]*\\)${SP}${SP}*\\([0-9][0-9]*\\)/SYS_\\1=\\2/g" \
|
||||
< $1 | grep -v SYS__
|
||||
|
||||
cat <<EOF
|
||||
)
|
||||
EOF
|
||||
22
src/syscall/mmap_unix_test.go
Normal file
22
src/syscall/mmap_unix_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2014 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_test
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMmap(t *testing.T) {
|
||||
b, err := syscall.Mmap(-1, 0, syscall.Getpagesize(), syscall.PROT_NONE, syscall.MAP_ANON|syscall.MAP_PRIVATE)
|
||||
if err != nil {
|
||||
t.Fatalf("Mmap: %v", err)
|
||||
}
|
||||
if err := syscall.Munmap(b); err != nil {
|
||||
t.Fatalf("Munmap: %v", err)
|
||||
}
|
||||
}
|
||||
34
src/syscall/net.go
Normal file
34
src/syscall/net.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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.
|
||||
|
||||
package syscall
|
||||
|
||||
// A RawConn is a raw network connection.
|
||||
type RawConn interface {
|
||||
// Control invokes f on the underlying connection's file
|
||||
// descriptor or handle.
|
||||
// The file descriptor fd is guaranteed to remain valid while
|
||||
// f executes but not after f returns.
|
||||
Control(f func(fd uintptr)) error
|
||||
|
||||
// Read invokes f on the underlying connection's file
|
||||
// descriptor or handle; f is expected to try to read from the
|
||||
// file descriptor.
|
||||
// If f returns true, Read returns. Otherwise Read blocks
|
||||
// waiting for the connection to be ready for reading and
|
||||
// tries again repeatedly.
|
||||
// The file descriptor is guaranteed to remain valid while f
|
||||
// executes but not after f returns.
|
||||
Read(f func(fd uintptr) (done bool)) error
|
||||
|
||||
// Write is like Read but for writing.
|
||||
Write(f func(fd uintptr) (done bool)) error
|
||||
}
|
||||
|
||||
// Conn is implemented by some types in the net and os packages to provide
|
||||
// access to the underlying file descriptor or handle.
|
||||
type Conn interface {
|
||||
// SyscallConn returns a raw network connection.
|
||||
SyscallConn() (RawConn, error)
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user