diff options
Diffstat (limited to 'src/runtime/cgo')
81 files changed, 5212 insertions, 0 deletions
diff --git a/src/runtime/cgo/abi_amd64.h b/src/runtime/cgo/abi_amd64.h new file mode 100644 index 0000000..9949435 --- /dev/null +++ b/src/runtime/cgo/abi_amd64.h @@ -0,0 +1,99 @@ +// 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. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These save the frame pointer, so in general, functions that use +// these should have zero frame size to suppress the automatic frame +// pointer, though it's harmless to not do this. + +#ifdef GOOS_windows + +// REGS_HOST_TO_ABI0_STACK is the stack bytes used by +// PUSH_REGS_HOST_TO_ABI0. +#define REGS_HOST_TO_ABI0_STACK (28*8 + 8) + +// PUSH_REGS_HOST_TO_ABI0 prepares for transitioning from +// the host ABI to Go ABI0 code. It saves all registers that are +// callee-save in the host ABI and caller-save in Go ABI0 and prepares +// for entry to Go. +// +// Save DI SI BP BX R12 R13 R14 R15 X6-X15 registers and the DF flag. +// Clear the DF flag for the Go ABI. +// MXCSR matches the Go ABI, so we don't have to set that, +// and Go doesn't modify it, so we don't have to save it. +#define PUSH_REGS_HOST_TO_ABI0() \ + PUSHFQ \ + CLD \ + ADJSP $(REGS_HOST_TO_ABI0_STACK - 8) \ + MOVQ DI, (0*0)(SP) \ + MOVQ SI, (1*8)(SP) \ + MOVQ BP, (2*8)(SP) \ + MOVQ BX, (3*8)(SP) \ + MOVQ R12, (4*8)(SP) \ + MOVQ R13, (5*8)(SP) \ + MOVQ R14, (6*8)(SP) \ + MOVQ R15, (7*8)(SP) \ + MOVUPS X6, (8*8)(SP) \ + MOVUPS X7, (10*8)(SP) \ + MOVUPS X8, (12*8)(SP) \ + MOVUPS X9, (14*8)(SP) \ + MOVUPS X10, (16*8)(SP) \ + MOVUPS X11, (18*8)(SP) \ + MOVUPS X12, (20*8)(SP) \ + MOVUPS X13, (22*8)(SP) \ + MOVUPS X14, (24*8)(SP) \ + MOVUPS X15, (26*8)(SP) + +#define POP_REGS_HOST_TO_ABI0() \ + MOVQ (0*0)(SP), DI \ + MOVQ (1*8)(SP), SI \ + MOVQ (2*8)(SP), BP \ + MOVQ (3*8)(SP), BX \ + MOVQ (4*8)(SP), R12 \ + MOVQ (5*8)(SP), R13 \ + MOVQ (6*8)(SP), R14 \ + MOVQ (7*8)(SP), R15 \ + MOVUPS (8*8)(SP), X6 \ + MOVUPS (10*8)(SP), X7 \ + MOVUPS (12*8)(SP), X8 \ + MOVUPS (14*8)(SP), X9 \ + MOVUPS (16*8)(SP), X10 \ + MOVUPS (18*8)(SP), X11 \ + MOVUPS (20*8)(SP), X12 \ + MOVUPS (22*8)(SP), X13 \ + MOVUPS (24*8)(SP), X14 \ + MOVUPS (26*8)(SP), X15 \ + ADJSP $-(REGS_HOST_TO_ABI0_STACK - 8) \ + POPFQ + +#else +// SysV ABI + +#define REGS_HOST_TO_ABI0_STACK (6*8) + +// SysV MXCSR matches the Go ABI, so we don't have to set that, +// and Go doesn't modify it, so we don't have to save it. +// Both SysV and Go require DF to be cleared, so that's already clear. +// The SysV and Go frame pointer conventions are compatible. +#define PUSH_REGS_HOST_TO_ABI0() \ + ADJSP $(REGS_HOST_TO_ABI0_STACK) \ + MOVQ BP, (5*8)(SP) \ + LEAQ (5*8)(SP), BP \ + MOVQ BX, (0*8)(SP) \ + MOVQ R12, (1*8)(SP) \ + MOVQ R13, (2*8)(SP) \ + MOVQ R14, (3*8)(SP) \ + MOVQ R15, (4*8)(SP) + +#define POP_REGS_HOST_TO_ABI0() \ + MOVQ (0*8)(SP), BX \ + MOVQ (1*8)(SP), R12 \ + MOVQ (2*8)(SP), R13 \ + MOVQ (3*8)(SP), R14 \ + MOVQ (4*8)(SP), R15 \ + MOVQ (5*8)(SP), BP \ + ADJSP $-(REGS_HOST_TO_ABI0_STACK) + +#endif diff --git a/src/runtime/cgo/abi_arm64.h b/src/runtime/cgo/abi_arm64.h new file mode 100644 index 0000000..e2b5e6d --- /dev/null +++ b/src/runtime/cgo/abi_arm64.h @@ -0,0 +1,43 @@ +// 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. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These macros save and restore the callee-saved registers +// from the stack, but they don't adjust stack pointer, so +// the user should prepare stack space in advance. +// SAVE_R19_TO_R28(offset) saves R19 ~ R28 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+9*8)(RSP). +// +// SAVE_F8_TO_F15(offset) saves F8 ~ F15 to the stack space +// of ((offset)+0*8)(RSP) ~ ((offset)+7*8)(RSP). +// +// R29 is not saved because Go will save and restore it. + +#define SAVE_R19_TO_R28(offset) \ + STP (R19, R20), ((offset)+0*8)(RSP) \ + STP (R21, R22), ((offset)+2*8)(RSP) \ + STP (R23, R24), ((offset)+4*8)(RSP) \ + STP (R25, R26), ((offset)+6*8)(RSP) \ + STP (R27, g), ((offset)+8*8)(RSP) + +#define RESTORE_R19_TO_R28(offset) \ + LDP ((offset)+0*8)(RSP), (R19, R20) \ + LDP ((offset)+2*8)(RSP), (R21, R22) \ + LDP ((offset)+4*8)(RSP), (R23, R24) \ + LDP ((offset)+6*8)(RSP), (R25, R26) \ + LDP ((offset)+8*8)(RSP), (R27, g) /* R28 */ + +#define SAVE_F8_TO_F15(offset) \ + FSTPD (F8, F9), ((offset)+0*8)(RSP) \ + FSTPD (F10, F11), ((offset)+2*8)(RSP) \ + FSTPD (F12, F13), ((offset)+4*8)(RSP) \ + FSTPD (F14, F15), ((offset)+6*8)(RSP) + +#define RESTORE_F8_TO_F15(offset) \ + FLDPD ((offset)+0*8)(RSP), (F8, F9) \ + FLDPD ((offset)+2*8)(RSP), (F10, F11) \ + FLDPD ((offset)+4*8)(RSP), (F12, F13) \ + FLDPD ((offset)+6*8)(RSP), (F14, F15) + diff --git a/src/runtime/cgo/abi_loong64.h b/src/runtime/cgo/abi_loong64.h new file mode 100644 index 0000000..b10d837 --- /dev/null +++ b/src/runtime/cgo/abi_loong64.h @@ -0,0 +1,60 @@ +// 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. + +// Macros for transitioning from the host ABI to Go ABI0. +// +// These macros save and restore the callee-saved registers +// from the stack, but they don't adjust stack pointer, so +// the user should prepare stack space in advance. +// SAVE_R22_TO_R31(offset) saves R22 ~ R31 to the stack space +// of ((offset)+0*8)(R3) ~ ((offset)+9*8)(R3). +// +// SAVE_F24_TO_F31(offset) saves F24 ~ F31 to the stack space +// of ((offset)+0*8)(R3) ~ ((offset)+7*8)(R3). +// +// Note: g is R22 + +#define SAVE_R22_TO_R31(offset) \ + MOVV g, ((offset)+(0*8))(R3) \ + MOVV R23, ((offset)+(1*8))(R3) \ + MOVV R24, ((offset)+(2*8))(R3) \ + MOVV R25, ((offset)+(3*8))(R3) \ + MOVV R26, ((offset)+(4*8))(R3) \ + MOVV R27, ((offset)+(5*8))(R3) \ + MOVV R28, ((offset)+(6*8))(R3) \ + MOVV R29, ((offset)+(7*8))(R3) \ + MOVV R30, ((offset)+(8*8))(R3) \ + MOVV R31, ((offset)+(9*8))(R3) + +#define SAVE_F24_TO_F31(offset) \ + MOVD F24, ((offset)+(0*8))(R3) \ + MOVD F25, ((offset)+(1*8))(R3) \ + MOVD F26, ((offset)+(2*8))(R3) \ + MOVD F27, ((offset)+(3*8))(R3) \ + MOVD F28, ((offset)+(4*8))(R3) \ + MOVD F29, ((offset)+(5*8))(R3) \ + MOVD F30, ((offset)+(6*8))(R3) \ + MOVD F31, ((offset)+(7*8))(R3) + +#define RESTORE_R22_TO_R31(offset) \ + MOVV ((offset)+(0*8))(R3), g \ + MOVV ((offset)+(1*8))(R3), R23 \ + MOVV ((offset)+(2*8))(R3), R24 \ + MOVV ((offset)+(3*8))(R3), R25 \ + MOVV ((offset)+(4*8))(R3), R26 \ + MOVV ((offset)+(5*8))(R3), R27 \ + MOVV ((offset)+(6*8))(R3), R28 \ + MOVV ((offset)+(7*8))(R3), R29 \ + MOVV ((offset)+(8*8))(R3), R30 \ + MOVV ((offset)+(9*8))(R3), R31 + +#define RESTORE_F24_TO_F31(offset) \ + MOVD ((offset)+(0*8))(R3), F24 \ + MOVD ((offset)+(1*8))(R3), F25 \ + MOVD ((offset)+(2*8))(R3), F26 \ + MOVD ((offset)+(3*8))(R3), F27 \ + MOVD ((offset)+(4*8))(R3), F28 \ + MOVD ((offset)+(5*8))(R3), F29 \ + MOVD ((offset)+(6*8))(R3), F30 \ + MOVD ((offset)+(7*8))(R3), F31 diff --git a/src/runtime/cgo/abi_ppc64x.h b/src/runtime/cgo/abi_ppc64x.h new file mode 100644 index 0000000..245a526 --- /dev/null +++ b/src/runtime/cgo/abi_ppc64x.h @@ -0,0 +1,195 @@ +// 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. + +// Macros for transitioning from the host ABI to Go ABI +// +// On PPC64/ELFv2 targets, the following registers are callee +// saved when called from C. They must be preserved before +// calling into Go which does not preserve any of them. +// +// R14-R31 +// CR2-4 +// VR20-31 +// F14-F31 +// +// xcoff(aix) and ELFv1 are similar, but may only require a +// subset of these. +// +// These macros assume a 16 byte aligned stack pointer. This +// is required by ELFv1, ELFv2, and AIX PPC64. + +#define SAVE_GPR_SIZE (18*8) +#define SAVE_GPR(offset) \ + MOVD R14, (offset+8*0)(R1) \ + MOVD R15, (offset+8*1)(R1) \ + MOVD R16, (offset+8*2)(R1) \ + MOVD R17, (offset+8*3)(R1) \ + MOVD R18, (offset+8*4)(R1) \ + MOVD R19, (offset+8*5)(R1) \ + MOVD R20, (offset+8*6)(R1) \ + MOVD R21, (offset+8*7)(R1) \ + MOVD R22, (offset+8*8)(R1) \ + MOVD R23, (offset+8*9)(R1) \ + MOVD R24, (offset+8*10)(R1) \ + MOVD R25, (offset+8*11)(R1) \ + MOVD R26, (offset+8*12)(R1) \ + MOVD R27, (offset+8*13)(R1) \ + MOVD R28, (offset+8*14)(R1) \ + MOVD R29, (offset+8*15)(R1) \ + MOVD g, (offset+8*16)(R1) \ + MOVD R31, (offset+8*17)(R1) + +#define RESTORE_GPR(offset) \ + MOVD (offset+8*0)(R1), R14 \ + MOVD (offset+8*1)(R1), R15 \ + MOVD (offset+8*2)(R1), R16 \ + MOVD (offset+8*3)(R1), R17 \ + MOVD (offset+8*4)(R1), R18 \ + MOVD (offset+8*5)(R1), R19 \ + MOVD (offset+8*6)(R1), R20 \ + MOVD (offset+8*7)(R1), R21 \ + MOVD (offset+8*8)(R1), R22 \ + MOVD (offset+8*9)(R1), R23 \ + MOVD (offset+8*10)(R1), R24 \ + MOVD (offset+8*11)(R1), R25 \ + MOVD (offset+8*12)(R1), R26 \ + MOVD (offset+8*13)(R1), R27 \ + MOVD (offset+8*14)(R1), R28 \ + MOVD (offset+8*15)(R1), R29 \ + MOVD (offset+8*16)(R1), g \ + MOVD (offset+8*17)(R1), R31 + +#define SAVE_FPR_SIZE (18*8) +#define SAVE_FPR(offset) \ + FMOVD F14, (offset+8*0)(R1) \ + FMOVD F15, (offset+8*1)(R1) \ + FMOVD F16, (offset+8*2)(R1) \ + FMOVD F17, (offset+8*3)(R1) \ + FMOVD F18, (offset+8*4)(R1) \ + FMOVD F19, (offset+8*5)(R1) \ + FMOVD F20, (offset+8*6)(R1) \ + FMOVD F21, (offset+8*7)(R1) \ + FMOVD F22, (offset+8*8)(R1) \ + FMOVD F23, (offset+8*9)(R1) \ + FMOVD F24, (offset+8*10)(R1) \ + FMOVD F25, (offset+8*11)(R1) \ + FMOVD F26, (offset+8*12)(R1) \ + FMOVD F27, (offset+8*13)(R1) \ + FMOVD F28, (offset+8*14)(R1) \ + FMOVD F29, (offset+8*15)(R1) \ + FMOVD F30, (offset+8*16)(R1) \ + FMOVD F31, (offset+8*17)(R1) + +#define RESTORE_FPR(offset) \ + FMOVD (offset+8*0)(R1), F14 \ + FMOVD (offset+8*1)(R1), F15 \ + FMOVD (offset+8*2)(R1), F16 \ + FMOVD (offset+8*3)(R1), F17 \ + FMOVD (offset+8*4)(R1), F18 \ + FMOVD (offset+8*5)(R1), F19 \ + FMOVD (offset+8*6)(R1), F20 \ + FMOVD (offset+8*7)(R1), F21 \ + FMOVD (offset+8*8)(R1), F22 \ + FMOVD (offset+8*9)(R1), F23 \ + FMOVD (offset+8*10)(R1), F24 \ + FMOVD (offset+8*11)(R1), F25 \ + FMOVD (offset+8*12)(R1), F26 \ + FMOVD (offset+8*13)(R1), F27 \ + FMOVD (offset+8*14)(R1), F28 \ + FMOVD (offset+8*15)(R1), F29 \ + FMOVD (offset+8*16)(R1), F30 \ + FMOVD (offset+8*17)(R1), F31 + +// Save and restore VR20-31 (aka VSR56-63). These +// macros must point to a 16B aligned offset. +#define SAVE_VR_SIZE (12*16) +#define SAVE_VR(offset, rtmp) \ + MOVD $(offset+16*0), rtmp \ + STVX V20, (rtmp)(R1) \ + MOVD $(offset+16*1), rtmp \ + STVX V21, (rtmp)(R1) \ + MOVD $(offset+16*2), rtmp \ + STVX V22, (rtmp)(R1) \ + MOVD $(offset+16*3), rtmp \ + STVX V23, (rtmp)(R1) \ + MOVD $(offset+16*4), rtmp \ + STVX V24, (rtmp)(R1) \ + MOVD $(offset+16*5), rtmp \ + STVX V25, (rtmp)(R1) \ + MOVD $(offset+16*6), rtmp \ + STVX V26, (rtmp)(R1) \ + MOVD $(offset+16*7), rtmp \ + STVX V27, (rtmp)(R1) \ + MOVD $(offset+16*8), rtmp \ + STVX V28, (rtmp)(R1) \ + MOVD $(offset+16*9), rtmp \ + STVX V29, (rtmp)(R1) \ + MOVD $(offset+16*10), rtmp \ + STVX V30, (rtmp)(R1) \ + MOVD $(offset+16*11), rtmp \ + STVX V31, (rtmp)(R1) + +#define RESTORE_VR(offset, rtmp) \ + MOVD $(offset+16*0), rtmp \ + LVX (rtmp)(R1), V20 \ + MOVD $(offset+16*1), rtmp \ + LVX (rtmp)(R1), V21 \ + MOVD $(offset+16*2), rtmp \ + LVX (rtmp)(R1), V22 \ + MOVD $(offset+16*3), rtmp \ + LVX (rtmp)(R1), V23 \ + MOVD $(offset+16*4), rtmp \ + LVX (rtmp)(R1), V24 \ + MOVD $(offset+16*5), rtmp \ + LVX (rtmp)(R1), V25 \ + MOVD $(offset+16*6), rtmp \ + LVX (rtmp)(R1), V26 \ + MOVD $(offset+16*7), rtmp \ + LVX (rtmp)(R1), V27 \ + MOVD $(offset+16*8), rtmp \ + LVX (rtmp)(R1), V28 \ + MOVD $(offset+16*9), rtmp \ + LVX (rtmp)(R1), V29 \ + MOVD $(offset+16*10), rtmp \ + LVX (rtmp)(R1), V30 \ + MOVD $(offset+16*11), rtmp \ + LVX (rtmp)(R1), V31 + +// LR and CR are saved in the caller's frame. The callee must +// make space for all other callee-save registers. +#define SAVE_ALL_REG_SIZE (SAVE_GPR_SIZE+SAVE_FPR_SIZE+SAVE_VR_SIZE) + +// Stack a frame and save all callee-save registers following the +// host OS's ABI. Fortunately, this is identical for AIX, ELFv1, and +// ELFv2. All host ABIs require the stack pointer to maintain 16 byte +// alignment, and save the callee-save registers in the same places. +// +// To restate, R1 is assumed to be aligned when this macro is used. +// This assumes the caller's frame is compliant with the host ABI. +// CR and LR are saved into the caller's frame per the host ABI. +// R0 is initialized to $0 as expected by Go. +#define STACK_AND_SAVE_HOST_TO_GO_ABI(extra) \ + MOVD LR, R0 \ + MOVD R0, 16(R1) \ + MOVW CR, R0 \ + MOVD R0, 8(R1) \ + MOVDU R1, -(extra)-FIXED_FRAME-SAVE_ALL_REG_SIZE(R1) \ + SAVE_GPR(extra+FIXED_FRAME) \ + SAVE_FPR(extra+FIXED_FRAME+SAVE_GPR_SIZE) \ + SAVE_VR(extra+FIXED_FRAME+SAVE_GPR_SIZE+SAVE_FPR_SIZE, R0) \ + MOVD $0, R0 + +// This unstacks the frame, restoring all callee-save registers +// as saved by STACK_AND_SAVE_HOST_TO_GO_ABI. +// +// R0 is not guaranteed to contain $0 after this macro. +#define UNSTACK_AND_RESTORE_GO_TO_HOST_ABI(extra) \ + RESTORE_GPR(extra+FIXED_FRAME) \ + RESTORE_FPR(extra+FIXED_FRAME+SAVE_GPR_SIZE) \ + RESTORE_VR(extra+FIXED_FRAME+SAVE_GPR_SIZE+SAVE_FPR_SIZE, R0) \ + ADD $(extra+FIXED_FRAME+SAVE_ALL_REG_SIZE), R1 \ + MOVD 16(R1), R0 \ + MOVD R0, LR \ + MOVD 8(R1), R0 \ + MOVW R0, CR diff --git a/src/runtime/cgo/asm_386.s b/src/runtime/cgo/asm_386.s new file mode 100644 index 0000000..f9a662a --- /dev/null +++ b/src/runtime/cgo/asm_386.s @@ -0,0 +1,42 @@ +// 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" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVL _crosscall2_ptr(SB), AX + MOVL $crosscall2_trampoline<>(SB), BX + MOVL BX, (AX) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT,$28-16 + MOVL BP, 24(SP) + MOVL BX, 20(SP) + MOVL SI, 16(SP) + MOVL DI, 12(SP) + + MOVL ctxt+12(FP), AX + MOVL AX, 8(SP) + MOVL a+4(FP), AX + MOVL AX, 4(SP) + MOVL fn+0(FP), AX + MOVL AX, 0(SP) + CALL runtime·cgocallback(SB) + + MOVL 12(SP), DI + MOVL 16(SP), SI + MOVL 20(SP), BX + MOVL 24(SP), BP + RET diff --git a/src/runtime/cgo/asm_amd64.s b/src/runtime/cgo/asm_amd64.s new file mode 100644 index 0000000..6bf1363 --- /dev/null +++ b/src/runtime/cgo/asm_amd64.s @@ -0,0 +1,47 @@ +// 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 "abi_amd64.h" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVQ _crosscall2_ptr(SB), AX + MOVQ $crosscall2_trampoline<>(SB), BX + MOVQ BX, (AX) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +// This signature is known to SWIG, so we can't change it. +TEXT crosscall2(SB),NOSPLIT,$0-0 + PUSH_REGS_HOST_TO_ABI0() + + // Make room for arguments to cgocallback. + ADJSP $0x18 +#ifndef GOOS_windows + MOVQ DI, 0x0(SP) /* fn */ + MOVQ SI, 0x8(SP) /* arg */ + // Skip n in DX. + MOVQ CX, 0x10(SP) /* ctxt */ +#else + MOVQ CX, 0x0(SP) /* fn */ + MOVQ DX, 0x8(SP) /* arg */ + // Skip n in R8. + MOVQ R9, 0x10(SP) /* ctxt */ +#endif + + CALL runtime·cgocallback(SB) + + ADJSP $-0x18 + POP_REGS_HOST_TO_ABI0() + RET diff --git a/src/runtime/cgo/asm_arm.s b/src/runtime/cgo/asm_arm.s new file mode 100644 index 0000000..425899e --- /dev/null +++ b/src/runtime/cgo/asm_arm.s @@ -0,0 +1,69 @@ +// 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" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVW _crosscall2_ptr(SB), R1 + MOVW $crosscall2_trampoline<>(SB), R2 + MOVW R2, (R1) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + SUB $(8*9), R13 // Reserve space for the floating point registers. + // The C arguments arrive in R0, R1, R2, and R3. We want to + // pass R0, R1, and R3 to Go, so we push those on the stack. + // Also, save C callee-save registers R4-R12. + MOVM.WP [R0, R1, R3, R4, R5, R6, R7, R8, R9, g, R11, R12], (R13) + // Finally, save the link register R14. This also puts the + // arguments we pushed for cgocallback where they need to be, + // starting at 4(R13). + MOVW.W R14, -4(R13) + + // Skip floating point registers if goarmsoftfp!=0. + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE skipfpsave + MOVD F8, (13*4+8*1)(R13) + MOVD F9, (13*4+8*2)(R13) + MOVD F10, (13*4+8*3)(R13) + MOVD F11, (13*4+8*4)(R13) + MOVD F12, (13*4+8*5)(R13) + MOVD F13, (13*4+8*6)(R13) + MOVD F14, (13*4+8*7)(R13) + MOVD F15, (13*4+8*8)(R13) + +skipfpsave: + BL runtime·load_g(SB) + // We set up the arguments to cgocallback when saving registers above. + BL runtime·cgocallback(SB) + + MOVB runtime·goarmsoftfp(SB), R11 + CMP $0, R11 + BNE skipfprest + MOVD (13*4+8*1)(R13), F8 + MOVD (13*4+8*2)(R13), F9 + MOVD (13*4+8*3)(R13), F10 + MOVD (13*4+8*4)(R13), F11 + MOVD (13*4+8*5)(R13), F12 + MOVD (13*4+8*6)(R13), F13 + MOVD (13*4+8*7)(R13), F14 + MOVD (13*4+8*8)(R13), F15 + +skipfprest: + MOVW.P 4(R13), R14 + MOVM.IAW (R13), [R0, R1, R3, R4, R5, R6, R7, R8, R9, g, R11, R12] + ADD $(8*9), R13 + MOVW R14, R15 diff --git a/src/runtime/cgo/asm_arm64.s b/src/runtime/cgo/asm_arm64.s new file mode 100644 index 0000000..5492dc1 --- /dev/null +++ b/src/runtime/cgo/asm_arm64.s @@ -0,0 +1,50 @@ +// 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" +#include "abi_arm64.h" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVD _crosscall2_ptr(SB), R1 + MOVD $crosscall2_trampoline<>(SB), R2 + MOVD R2, (R1) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * We still need to save all callee save register as before, and then + * push 3 args for fn (R0, R1, R3), skipping R2. + * Also note that at procedure entry in gc world, 8(RSP) will be the + * first arg. + */ + SUB $(8*24), RSP + STP (R0, R1), (8*1)(RSP) + MOVD R3, (8*3)(RSP) + + SAVE_R19_TO_R28(8*4) + SAVE_F8_TO_F15(8*14) + STP (R29, R30), (8*22)(RSP) + + + // Initialize Go ABI environment + BL runtime·load_g(SB) + BL runtime·cgocallback(SB) + + RESTORE_R19_TO_R28(8*4) + RESTORE_F8_TO_F15(8*14) + LDP (8*22)(RSP), (R29, R30) + + ADD $(8*24), RSP + RET diff --git a/src/runtime/cgo/asm_loong64.s b/src/runtime/cgo/asm_loong64.s new file mode 100644 index 0000000..19c8d74 --- /dev/null +++ b/src/runtime/cgo/asm_loong64.s @@ -0,0 +1,53 @@ +// 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" +#include "abi_loong64.h" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVV _crosscall2_ptr(SB), R5 + MOVV $crosscall2_trampoline<>(SB), R6 + MOVV R6, (R5) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * We still need to save all callee save register as before, and then + * push 3 args for fn (R4, R5, R7), skipping R6. + * Also note that at procedure entry in gc world, 8(R29) will be the + * first arg. + */ + + ADDV $(-23*8), R3 + MOVV R4, (1*8)(R3) // fn unsafe.Pointer + MOVV R5, (2*8)(R3) // a unsafe.Pointer + MOVV R7, (3*8)(R3) // ctxt uintptr + + SAVE_R22_TO_R31((4*8)) + SAVE_F24_TO_F31((14*8)) + MOVV R1, (22*8)(R3) + + // Initialize Go ABI environment + JAL runtime·load_g(SB) + + JAL runtime·cgocallback(SB) + + RESTORE_R22_TO_R31((4*8)) + RESTORE_F24_TO_F31((14*8)) + MOVV (22*8)(R3), R1 + + ADDV $(23*8), R3 + + RET diff --git a/src/runtime/cgo/asm_mips64x.s b/src/runtime/cgo/asm_mips64x.s new file mode 100644 index 0000000..af817d5 --- /dev/null +++ b/src/runtime/cgo/asm_mips64x.s @@ -0,0 +1,95 @@ +// 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 mips64 || mips64le + +#include "textflag.h" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVV _crosscall2_ptr(SB), R5 + MOVV $crosscall2_trampoline<>(SB), R6 + MOVV R6, (R5) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * We still need to save all callee save register as before, and then + * push 3 args for fn (R4, R5, R7), skipping R6. + * Also note that at procedure entry in gc world, 8(R29) will be the + * first arg. + */ +#ifndef GOMIPS64_softfloat + ADDV $(-8*23), R29 +#else + ADDV $(-8*15), R29 +#endif + MOVV R4, (8*1)(R29) // fn unsafe.Pointer + MOVV R5, (8*2)(R29) // a unsafe.Pointer + MOVV R7, (8*3)(R29) // ctxt uintptr + MOVV R16, (8*4)(R29) + MOVV R17, (8*5)(R29) + MOVV R18, (8*6)(R29) + MOVV R19, (8*7)(R29) + MOVV R20, (8*8)(R29) + MOVV R21, (8*9)(R29) + MOVV R22, (8*10)(R29) + MOVV R23, (8*11)(R29) + MOVV RSB, (8*12)(R29) + MOVV g, (8*13)(R29) + MOVV R31, (8*14)(R29) +#ifndef GOMIPS64_softfloat + MOVD F24, (8*15)(R29) + MOVD F25, (8*16)(R29) + MOVD F26, (8*17)(R29) + MOVD F27, (8*18)(R29) + MOVD F28, (8*19)(R29) + MOVD F29, (8*20)(R29) + MOVD F30, (8*21)(R29) + MOVD F31, (8*22)(R29) +#endif + // Initialize Go ABI environment + // prepare SB register = PC & 0xffffffff00000000 + BGEZAL R0, 1(PC) + SRLV $32, R31, RSB + SLLV $32, RSB + JAL runtime·load_g(SB) + + JAL runtime·cgocallback(SB) + + MOVV (8*4)(R29), R16 + MOVV (8*5)(R29), R17 + MOVV (8*6)(R29), R18 + MOVV (8*7)(R29), R19 + MOVV (8*8)(R29), R20 + MOVV (8*9)(R29), R21 + MOVV (8*10)(R29), R22 + MOVV (8*11)(R29), R23 + MOVV (8*12)(R29), RSB + MOVV (8*13)(R29), g + MOVV (8*14)(R29), R31 +#ifndef GOMIPS64_softfloat + MOVD (8*15)(R29), F24 + MOVD (8*16)(R29), F25 + MOVD (8*17)(R29), F26 + MOVD (8*18)(R29), F27 + MOVD (8*19)(R29), F28 + MOVD (8*20)(R29), F29 + MOVD (8*21)(R29), F30 + MOVD (8*22)(R29), F31 + ADDV $(8*23), R29 +#else + ADDV $(8*15), R29 +#endif + RET diff --git a/src/runtime/cgo/asm_mipsx.s b/src/runtime/cgo/asm_mipsx.s new file mode 100644 index 0000000..198c59a --- /dev/null +++ b/src/runtime/cgo/asm_mipsx.s @@ -0,0 +1,88 @@ +// 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 mips || mipsle + +#include "textflag.h" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVW _crosscall2_ptr(SB), R5 + MOVW $crosscall2_trampoline<>(SB), R6 + MOVW R6, (R5) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * We still need to save all callee save register as before, and then + * push 3 args for fn (R4, R5, R7), skipping R6. + * Also note that at procedure entry in gc world, 4(R29) will be the + * first arg. + */ + + // Space for 9 caller-saved GPR + LR + 6 caller-saved FPR. + // O32 ABI allows us to smash 16 bytes argument area of caller frame. +#ifndef GOMIPS_softfloat + SUBU $(4*14+8*6-16), R29 +#else + SUBU $(4*14-16), R29 // For soft-float, no FPR. +#endif + MOVW R4, (4*1)(R29) // fn unsafe.Pointer + MOVW R5, (4*2)(R29) // a unsafe.Pointer + MOVW R7, (4*3)(R29) // ctxt uintptr + MOVW R16, (4*4)(R29) + MOVW R17, (4*5)(R29) + MOVW R18, (4*6)(R29) + MOVW R19, (4*7)(R29) + MOVW R20, (4*8)(R29) + MOVW R21, (4*9)(R29) + MOVW R22, (4*10)(R29) + MOVW R23, (4*11)(R29) + MOVW g, (4*12)(R29) + MOVW R31, (4*13)(R29) +#ifndef GOMIPS_softfloat + MOVD F20, (4*14)(R29) + MOVD F22, (4*14+8*1)(R29) + MOVD F24, (4*14+8*2)(R29) + MOVD F26, (4*14+8*3)(R29) + MOVD F28, (4*14+8*4)(R29) + MOVD F30, (4*14+8*5)(R29) +#endif + JAL runtime·load_g(SB) + + JAL runtime·cgocallback(SB) + + MOVW (4*4)(R29), R16 + MOVW (4*5)(R29), R17 + MOVW (4*6)(R29), R18 + MOVW (4*7)(R29), R19 + MOVW (4*8)(R29), R20 + MOVW (4*9)(R29), R21 + MOVW (4*10)(R29), R22 + MOVW (4*11)(R29), R23 + MOVW (4*12)(R29), g + MOVW (4*13)(R29), R31 +#ifndef GOMIPS_softfloat + MOVD (4*14)(R29), F20 + MOVD (4*14+8*1)(R29), F22 + MOVD (4*14+8*2)(R29), F24 + MOVD (4*14+8*3)(R29), F26 + MOVD (4*14+8*4)(R29), F28 + MOVD (4*14+8*5)(R29), F30 + + ADDU $(4*14+8*6-16), R29 +#else + ADDU $(4*14-16), R29 +#endif + RET diff --git a/src/runtime/cgo/asm_ppc64x.s b/src/runtime/cgo/asm_ppc64x.s new file mode 100644 index 0000000..a389745 --- /dev/null +++ b/src/runtime/cgo/asm_ppc64x.s @@ -0,0 +1,70 @@ +// 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 ppc64 || ppc64le + +#include "textflag.h" +#include "asm_ppc64x.h" +#include "abi_ppc64x.h" + +#ifdef GO_PPC64X_HAS_FUNCDESC +// crosscall2 is marked with go:cgo_export_static. On AIX, this creates and exports +// the symbol name and descriptor as the AIX linker expects, but does not work if +// referenced from within Go. Create and use an aliased descriptor of crosscall2 +// to workaround this. +DEFINE_PPC64X_FUNCDESC(_crosscall2<>, crosscall2) +#define CROSSCALL2_FPTR $_crosscall2<>(SB) +#else +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +#define CROSSCALL2_FPTR $crosscall2_trampoline<>(SB) +#endif + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVD _crosscall2_ptr(SB), R5 + MOVD CROSSCALL2_FPTR, R6 + MOVD R6, (R5) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +// The value of R2 is saved on the new stack frame, and not +// the caller's frame due to issue #43228. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + // Start with standard C stack frame layout and linkage, allocate + // 32 bytes of argument space, save callee-save regs, and set R0 to $0. + STACK_AND_SAVE_HOST_TO_GO_ABI(32) + // The above will not preserve R2 (TOC). Save it in case Go is + // compiled without a TOC pointer (e.g -buildmode=default). + MOVD R2, 24(R1) + + // Load the current g. + BL runtime·load_g(SB) + +#ifdef GO_PPC64X_HAS_FUNCDESC + // Load the real entry address from the first slot of the function descriptor. + // The first argument fn might be null, that means dropm in pthread key destructor. + CMP R3, $0 + BEQ nil_fn + MOVD 8(R3), R2 + MOVD (R3), R3 +nil_fn: +#endif + MOVD R3, FIXED_FRAME+0(R1) // fn unsafe.Pointer + MOVD R4, FIXED_FRAME+8(R1) // a unsafe.Pointer + // Skip R5 = n uint32 + MOVD R6, FIXED_FRAME+16(R1) // ctxt uintptr + BL runtime·cgocallback(SB) + + // Restore the old frame, and R2. + MOVD 24(R1), R2 + UNSTACK_AND_RESTORE_GO_TO_HOST_ABI(32) + RET diff --git a/src/runtime/cgo/asm_riscv64.s b/src/runtime/cgo/asm_riscv64.s new file mode 100644 index 0000000..d75a543 --- /dev/null +++ b/src/runtime/cgo/asm_riscv64.s @@ -0,0 +1,91 @@ +// 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" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOV _crosscall2_ptr(SB), X7 + MOV $crosscall2_trampoline<>(SB), X8 + MOV X8, (X7) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + /* + * Push arguments for fn (X10, X11, X13), along with all callee-save + * registers. Note that at procedure entry the first argument is at + * 8(X2). + */ + ADD $(-8*29), X2 + MOV X10, (8*1)(X2) // fn unsafe.Pointer + MOV X11, (8*2)(X2) // a unsafe.Pointer + MOV X13, (8*3)(X2) // ctxt uintptr + MOV X8, (8*4)(X2) + MOV X9, (8*5)(X2) + MOV X18, (8*6)(X2) + MOV X19, (8*7)(X2) + MOV X20, (8*8)(X2) + MOV X21, (8*9)(X2) + MOV X22, (8*10)(X2) + MOV X23, (8*11)(X2) + MOV X24, (8*12)(X2) + MOV X25, (8*13)(X2) + MOV X26, (8*14)(X2) + MOV g, (8*15)(X2) + MOV X1, (8*16)(X2) + MOVD F8, (8*17)(X2) + MOVD F9, (8*18)(X2) + MOVD F18, (8*19)(X2) + MOVD F19, (8*20)(X2) + MOVD F20, (8*21)(X2) + MOVD F21, (8*22)(X2) + MOVD F22, (8*23)(X2) + MOVD F23, (8*24)(X2) + MOVD F24, (8*25)(X2) + MOVD F25, (8*26)(X2) + MOVD F26, (8*27)(X2) + MOVD F27, (8*28)(X2) + + // Initialize Go ABI environment + CALL runtime·load_g(SB) + CALL runtime·cgocallback(SB) + + MOV (8*4)(X2), X8 + MOV (8*5)(X2), X9 + MOV (8*6)(X2), X18 + MOV (8*7)(X2), X19 + MOV (8*8)(X2), X20 + MOV (8*9)(X2), X21 + MOV (8*10)(X2), X22 + MOV (8*11)(X2), X23 + MOV (8*12)(X2), X24 + MOV (8*13)(X2), X25 + MOV (8*14)(X2), X26 + MOV (8*15)(X2), g + MOV (8*16)(X2), X1 + MOVD (8*17)(X2), F8 + MOVD (8*18)(X2), F9 + MOVD (8*19)(X2), F18 + MOVD (8*20)(X2), F19 + MOVD (8*21)(X2), F20 + MOVD (8*22)(X2), F21 + MOVD (8*23)(X2), F22 + MOVD (8*24)(X2), F23 + MOVD (8*25)(X2), F24 + MOVD (8*26)(X2), F25 + MOVD (8*27)(X2), F26 + MOVD (8*28)(X2), F27 + ADD $(8*29), X2 + + RET diff --git a/src/runtime/cgo/asm_s390x.s b/src/runtime/cgo/asm_s390x.s new file mode 100644 index 0000000..8f74fd5 --- /dev/null +++ b/src/runtime/cgo/asm_s390x.s @@ -0,0 +1,68 @@ +// 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" + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's such a pointer chain: _crosscall2_ptr -> x_crosscall2_ptr -> crosscall2 +// Use a local trampoline, to avoid taking the address of a dynamically exported +// function. +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + MOVD _crosscall2_ptr(SB), R1 + MOVD $crosscall2_trampoline<>(SB), R2 + MOVD R2, (R1) + RET + +TEXT crosscall2_trampoline<>(SB),NOSPLIT,$0-0 + JMP crosscall2(SB) + +// Called by C code generated by cmd/cgo. +// func crosscall2(fn, a unsafe.Pointer, n int32, ctxt uintptr) +// Saves C callee-saved registers and calls cgocallback with three arguments. +// fn is the PC of a func(a unsafe.Pointer) function. +TEXT crosscall2(SB),NOSPLIT|NOFRAME,$0 + // Start with standard C stack frame layout and linkage. + + // Save R6-R15 in the register save area of the calling function. + STMG R6, R15, 48(R15) + + // Allocate 96 bytes on the stack. + MOVD $-96(R15), R15 + + // Save F8-F15 in our stack frame. + FMOVD F8, 32(R15) + FMOVD F9, 40(R15) + FMOVD F10, 48(R15) + FMOVD F11, 56(R15) + FMOVD F12, 64(R15) + FMOVD F13, 72(R15) + FMOVD F14, 80(R15) + FMOVD F15, 88(R15) + + // Initialize Go ABI environment. + BL runtime·load_g(SB) + + MOVD R2, 8(R15) // fn unsafe.Pointer + MOVD R3, 16(R15) // a unsafe.Pointer + // Skip R4 = n uint32 + MOVD R5, 24(R15) // ctxt uintptr + BL runtime·cgocallback(SB) + + FMOVD 32(R15), F8 + FMOVD 40(R15), F9 + FMOVD 48(R15), F10 + FMOVD 56(R15), F11 + FMOVD 64(R15), F12 + FMOVD 72(R15), F13 + FMOVD 80(R15), F14 + FMOVD 88(R15), F15 + + // De-allocate stack frame. + MOVD $96(R15), R15 + + // Restore R6-R15. + LMG 48(R15), R6, R15 + + RET + diff --git a/src/runtime/cgo/asm_wasm.s b/src/runtime/cgo/asm_wasm.s new file mode 100644 index 0000000..e7f01bd --- /dev/null +++ b/src/runtime/cgo/asm_wasm.s @@ -0,0 +1,11 @@ +// 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. + +#include "textflag.h" + +TEXT ·set_crosscall2(SB),NOSPLIT,$0-0 + UNDEF + +TEXT crosscall2(SB), NOSPLIT, $0 + UNDEF diff --git a/src/runtime/cgo/callbacks.go b/src/runtime/cgo/callbacks.go new file mode 100644 index 0000000..3c246a8 --- /dev/null +++ b/src/runtime/cgo/callbacks.go @@ -0,0 +1,152 @@ +// 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 cgo + +import "unsafe" + +// These utility functions are available to be called from code +// compiled with gcc via crosscall2. + +// The declaration of crosscall2 is: +// void crosscall2(void (*fn)(void *), void *, int); +// +// We need to export the symbol crosscall2 in order to support +// callbacks from shared libraries. This applies regardless of +// linking mode. +// +// Compatibility note: SWIG uses crosscall2 in exactly one situation: +// to call _cgo_panic using the pattern shown below. We need to keep +// that pattern working. In particular, crosscall2 actually takes four +// arguments, but it works to call it with three arguments when +// calling _cgo_panic. +// +//go:cgo_export_static crosscall2 +//go:cgo_export_dynamic crosscall2 + +// Panic. The argument is converted into a Go string. + +// Call like this in code compiled with gcc: +// struct { const char *p; } a; +// a.p = /* string to pass to panic */; +// crosscall2(_cgo_panic, &a, sizeof a); +// /* The function call will not return. */ + +// TODO: We should export a regular C function to panic, change SWIG +// to use that instead of the above pattern, and then we can drop +// backwards-compatibility from crosscall2 and stop exporting it. + +//go:linkname _runtime_cgo_panic_internal runtime._cgo_panic_internal +func _runtime_cgo_panic_internal(p *byte) + +//go:linkname _cgo_panic _cgo_panic +//go:cgo_export_static _cgo_panic +//go:cgo_export_dynamic _cgo_panic +func _cgo_panic(a *struct{ cstr *byte }) { + _runtime_cgo_panic_internal(a.cstr) +} + +//go:cgo_import_static x_cgo_init +//go:linkname x_cgo_init x_cgo_init +//go:linkname _cgo_init _cgo_init +var x_cgo_init byte +var _cgo_init = &x_cgo_init + +//go:cgo_import_static x_cgo_thread_start +//go:linkname x_cgo_thread_start x_cgo_thread_start +//go:linkname _cgo_thread_start _cgo_thread_start +var x_cgo_thread_start byte +var _cgo_thread_start = &x_cgo_thread_start + +// Creates a new system thread without updating any Go state. +// +// This method is invoked during shared library loading to create a new OS +// thread to perform the runtime initialization. This method is similar to +// _cgo_sys_thread_start except that it doesn't update any Go state. + +//go:cgo_import_static x_cgo_sys_thread_create +//go:linkname x_cgo_sys_thread_create x_cgo_sys_thread_create +//go:linkname _cgo_sys_thread_create _cgo_sys_thread_create +var x_cgo_sys_thread_create byte +var _cgo_sys_thread_create = &x_cgo_sys_thread_create + +// Indicates whether a dummy thread key has been created or not. +// +// When calling go exported function from C, we register a destructor +// callback, for a dummy thread key, by using pthread_key_create. + +//go:cgo_import_static x_cgo_pthread_key_created +//go:linkname x_cgo_pthread_key_created x_cgo_pthread_key_created +//go:linkname _cgo_pthread_key_created _cgo_pthread_key_created +var x_cgo_pthread_key_created byte +var _cgo_pthread_key_created = &x_cgo_pthread_key_created + +// Export crosscall2 to a c function pointer variable. +// Used to dropm in pthread key destructor, while C thread is exiting. + +//go:cgo_import_static x_crosscall2_ptr +//go:linkname x_crosscall2_ptr x_crosscall2_ptr +//go:linkname _crosscall2_ptr _crosscall2_ptr +var x_crosscall2_ptr byte +var _crosscall2_ptr = &x_crosscall2_ptr + +// Set the x_crosscall2_ptr C function pointer variable point to crosscall2. +// It's for the runtime package to call at init time. +func set_crosscall2() + +//go:linkname _set_crosscall2 runtime.set_crosscall2 +var _set_crosscall2 = set_crosscall2 + +// Store the g into the thread-specific value. +// So that pthread_key_destructor will dropm when the thread is exiting. + +//go:cgo_import_static x_cgo_bindm +//go:linkname x_cgo_bindm x_cgo_bindm +//go:linkname _cgo_bindm _cgo_bindm +var x_cgo_bindm byte +var _cgo_bindm = &x_cgo_bindm + +// Notifies that the runtime has been initialized. +// +// We currently block at every CGO entry point (via _cgo_wait_runtime_init_done) +// to ensure that the runtime has been initialized before the CGO call is +// executed. This is necessary for shared libraries where we kickoff runtime +// initialization in a separate thread and return without waiting for this +// thread to complete the init. + +//go:cgo_import_static x_cgo_notify_runtime_init_done +//go:linkname x_cgo_notify_runtime_init_done x_cgo_notify_runtime_init_done +//go:linkname _cgo_notify_runtime_init_done _cgo_notify_runtime_init_done +var x_cgo_notify_runtime_init_done byte +var _cgo_notify_runtime_init_done = &x_cgo_notify_runtime_init_done + +// Sets the traceback context function. See runtime.SetCgoTraceback. + +//go:cgo_import_static x_cgo_set_context_function +//go:linkname x_cgo_set_context_function x_cgo_set_context_function +//go:linkname _cgo_set_context_function _cgo_set_context_function +var x_cgo_set_context_function byte +var _cgo_set_context_function = &x_cgo_set_context_function + +// Calls a libc function to execute background work injected via libc +// interceptors, such as processing pending signals under the thread +// sanitizer. +// +// Left as a nil pointer if no libc interceptors are expected. + +//go:cgo_import_static _cgo_yield +//go:linkname _cgo_yield _cgo_yield +var _cgo_yield unsafe.Pointer + +//go:cgo_export_static _cgo_topofstack +//go:cgo_export_dynamic _cgo_topofstack + +// x_cgo_getstackbound gets the thread's C stack size and +// set the G's stack bound based on the stack size. + +//go:cgo_import_static x_cgo_getstackbound +//go:linkname x_cgo_getstackbound x_cgo_getstackbound +//go:linkname _cgo_getstackbound _cgo_getstackbound +var x_cgo_getstackbound byte +var _cgo_getstackbound = &x_cgo_getstackbound diff --git a/src/runtime/cgo/callbacks_aix.go b/src/runtime/cgo/callbacks_aix.go new file mode 100644 index 0000000..8f756fb --- /dev/null +++ b/src/runtime/cgo/callbacks_aix.go @@ -0,0 +1,12 @@ +// 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. + +package cgo + +// These functions must be exported in order to perform +// longcall on cgo programs (cf gcc_aix_ppc64.c). +// +//go:cgo_export_static __cgo_topofstack +//go:cgo_export_static runtime.rt0_go +//go:cgo_export_static _rt0_ppc64_aix_lib diff --git a/src/runtime/cgo/callbacks_traceback.go b/src/runtime/cgo/callbacks_traceback.go new file mode 100644 index 0000000..dae31a8 --- /dev/null +++ b/src/runtime/cgo/callbacks_traceback.go @@ -0,0 +1,17 @@ +// 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 darwin || linux + +package cgo + +import _ "unsafe" // for go:linkname + +// Calls the traceback function passed to SetCgoTraceback. + +//go:cgo_import_static x_cgo_callers +//go:linkname x_cgo_callers x_cgo_callers +//go:linkname _cgo_callers _cgo_callers +var x_cgo_callers byte +var _cgo_callers = &x_cgo_callers diff --git a/src/runtime/cgo/cgo.go b/src/runtime/cgo/cgo.go new file mode 100644 index 0000000..1e3a502 --- /dev/null +++ b/src/runtime/cgo/cgo.go @@ -0,0 +1,40 @@ +// 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. + +/* +Package cgo contains runtime support for code generated +by the cgo tool. See the documentation for the cgo command +for details on using cgo. +*/ +package cgo + +/* + +#cgo darwin,!arm64 LDFLAGS: -lpthread +#cgo darwin,arm64 LDFLAGS: -framework CoreFoundation +#cgo dragonfly LDFLAGS: -lpthread +#cgo freebsd LDFLAGS: -lpthread +#cgo android LDFLAGS: -llog +#cgo !android,linux LDFLAGS: -lpthread +#cgo netbsd LDFLAGS: -lpthread +#cgo openbsd LDFLAGS: -lpthread +#cgo aix LDFLAGS: -Wl,-berok +#cgo solaris LDFLAGS: -lxnet +#cgo solaris LDFLAGS: -lsocket + +// Use -fno-stack-protector to avoid problems locating the +// proper support functions. See issues #52919, #54313, #58385. +#cgo CFLAGS: -Wall -Werror -fno-stack-protector + +#cgo solaris CPPFLAGS: -D_POSIX_PTHREAD_SEMANTICS + +*/ +import "C" + +import "runtime/internal/sys" + +// Incomplete is used specifically for the semantics of incomplete C types. +type Incomplete struct { + _ sys.NotInHeap +} diff --git a/src/runtime/cgo/dragonfly.go b/src/runtime/cgo/dragonfly.go new file mode 100644 index 0000000..36d70e3 --- /dev/null +++ b/src/runtime/cgo/dragonfly.go @@ -0,0 +1,19 @@ +// 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 dragonfly + +package cgo + +import _ "unsafe" // for go:linkname + +// Supply environ and __progname, because we don't +// link against the standard DragonFly crt0.o and the +// libc dynamic library needs them. + +//go:linkname _environ environ +//go:linkname _progname __progname + +var _environ uintptr +var _progname uintptr diff --git a/src/runtime/cgo/freebsd.go b/src/runtime/cgo/freebsd.go new file mode 100644 index 0000000..2d9f624 --- /dev/null +++ b/src/runtime/cgo/freebsd.go @@ -0,0 +1,22 @@ +// 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 freebsd + +package cgo + +import _ "unsafe" // for go:linkname + +// Supply environ and __progname, because we don't +// link against the standard FreeBSD crt0.o and the +// libc dynamic library needs them. + +//go:linkname _environ environ +//go:linkname _progname __progname + +//go:cgo_export_dynamic environ +//go:cgo_export_dynamic __progname + +var _environ uintptr +var _progname uintptr diff --git a/src/runtime/cgo/gcc_386.S b/src/runtime/cgo/gcc_386.S new file mode 100644 index 0000000..d4c5934 --- /dev/null +++ b/src/runtime/cgo/gcc_386.S @@ -0,0 +1,48 @@ +// 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. + +.file "gcc_386.S" + +/* + * Windows still insists on underscore prefixes for C function names. + */ +#if defined(_WIN32) +#define EXT(s) _##s +#else +#define EXT(s) s +#endif + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard x86 ABI, where %ebp, %ebx, %esi, + * and %edi are callee-save, so they must be saved explicitly. + */ +.globl EXT(crosscall1) +EXT(crosscall1): + pushl %ebp + movl %esp, %ebp + pushl %ebx + pushl %esi + pushl %edi + + movl 16(%ebp), %eax /* g */ + pushl %eax + movl 12(%ebp), %eax /* setg_gcc */ + call *%eax + popl %eax + + movl 8(%ebp), %eax /* fn */ + call *%eax + + popl %edi + popl %esi + popl %ebx + popl %ebp + ret + +#ifdef __ELF__ +.section .note.GNU-stack,"",@progbits +#endif diff --git a/src/runtime/cgo/gcc_aix_ppc64.S b/src/runtime/cgo/gcc_aix_ppc64.S new file mode 100644 index 0000000..6f465f0 --- /dev/null +++ b/src/runtime/cgo/gcc_aix_ppc64.S @@ -0,0 +1,132 @@ +// 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. + +.file "gcc_aix_ppc64.S" + +/* + * void crosscall_ppc64(void (*fn)(void), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard ppc64 C ABI, where r2, r14-r31, f14-f31 are + * callee-save, so they must be saved explicitly. + * AIX has a special assembly syntax and keywords that can be mixed with + * Linux assembly. + */ + .toc + .csect .text[PR] + .globl crosscall_ppc64 + .globl .crosscall_ppc64 + .csect crosscall_ppc64[DS] +crosscall_ppc64: + .llong .crosscall_ppc64, TOC[tc0], 0 + .csect .text[PR] +.crosscall_ppc64: + // Start with standard C stack frame layout and linkage + mflr 0 + std 0, 16(1) // Save LR in caller's frame + std 2, 40(1) // Save TOC in caller's frame + bl saveregs + stdu 1, -296(1) + + // Set up Go ABI constant registers + // Must match _cgo_reginit in runtime package. + xor 0, 0, 0 + + // Restore g pointer (r30 in Go ABI, which may have been clobbered by C) + mr 30, 4 + + // Call fn + mr 12, 3 + mtctr 12 + bctrl + + addi 1, 1, 296 + bl restoreregs + ld 2, 40(1) + ld 0, 16(1) + mtlr 0 + blr + +saveregs: + // Save callee-save registers + // O=-288; for R in {14..31}; do echo "\tstd\t$R, $O(1)"; ((O+=8)); done; for F in f{14..31}; do echo "\tstfd\t$F, $O(1)"; ((O+=8)); done + std 14, -288(1) + std 15, -280(1) + std 16, -272(1) + std 17, -264(1) + std 18, -256(1) + std 19, -248(1) + std 20, -240(1) + std 21, -232(1) + std 22, -224(1) + std 23, -216(1) + std 24, -208(1) + std 25, -200(1) + std 26, -192(1) + std 27, -184(1) + std 28, -176(1) + std 29, -168(1) + std 30, -160(1) + std 31, -152(1) + stfd 14, -144(1) + stfd 15, -136(1) + stfd 16, -128(1) + stfd 17, -120(1) + stfd 18, -112(1) + stfd 19, -104(1) + stfd 20, -96(1) + stfd 21, -88(1) + stfd 22, -80(1) + stfd 23, -72(1) + stfd 24, -64(1) + stfd 25, -56(1) + stfd 26, -48(1) + stfd 27, -40(1) + stfd 28, -32(1) + stfd 29, -24(1) + stfd 30, -16(1) + stfd 31, -8(1) + + blr + +restoreregs: + // O=-288; for R in {14..31}; do echo "\tld\t$R, $O(1)"; ((O+=8)); done; for F in {14..31}; do echo "\tlfd\t$F, $O(1)"; ((O+=8)); done + ld 14, -288(1) + ld 15, -280(1) + ld 16, -272(1) + ld 17, -264(1) + ld 18, -256(1) + ld 19, -248(1) + ld 20, -240(1) + ld 21, -232(1) + ld 22, -224(1) + ld 23, -216(1) + ld 24, -208(1) + ld 25, -200(1) + ld 26, -192(1) + ld 27, -184(1) + ld 28, -176(1) + ld 29, -168(1) + ld 30, -160(1) + ld 31, -152(1) + lfd 14, -144(1) + lfd 15, -136(1) + lfd 16, -128(1) + lfd 17, -120(1) + lfd 18, -112(1) + lfd 19, -104(1) + lfd 20, -96(1) + lfd 21, -88(1) + lfd 22, -80(1) + lfd 23, -72(1) + lfd 24, -64(1) + lfd 25, -56(1) + lfd 26, -48(1) + lfd 27, -40(1) + lfd 28, -32(1) + lfd 29, -24(1) + lfd 30, -16(1) + lfd 31, -8(1) + + blr diff --git a/src/runtime/cgo/gcc_aix_ppc64.c b/src/runtime/cgo/gcc_aix_ppc64.c new file mode 100644 index 0000000..9dd9524 --- /dev/null +++ b/src/runtime/cgo/gcc_aix_ppc64.c @@ -0,0 +1,35 @@ +// 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. + +/* + * On AIX, call to _cgo_topofstack and Go main are forced to be a longcall. + * Without it, ld might add trampolines in the middle of .text section + * to reach these functions which are normally declared in runtime package. + */ +extern int __attribute__((longcall)) __cgo_topofstack(void); +extern int __attribute__((longcall)) runtime_rt0_go(int argc, char **argv); +extern void __attribute__((longcall)) _rt0_ppc64_aix_lib(void); + +int _cgo_topofstack(void) { + return __cgo_topofstack(); +} + +int main(int argc, char **argv) { + return runtime_rt0_go(argc, argv); +} + +static void libinit(void) __attribute__ ((constructor)); + +/* + * libinit aims to replace .init_array section which isn't available on aix. + * Using __attribute__ ((constructor)) let gcc handles this instead of + * adding special code in cmd/link. + * However, it will be called for every Go programs which has cgo. + * Inside _rt0_ppc64_aix_lib(), runtime.isarchive is checked in order + * to know if this program is a c-archive or a simple cgo program. + * If it's not set, _rt0_ppc64_ax_lib() returns directly. + */ +static void libinit() { + _rt0_ppc64_aix_lib(); +} diff --git a/src/runtime/cgo/gcc_amd64.S b/src/runtime/cgo/gcc_amd64.S new file mode 100644 index 0000000..3ba793a --- /dev/null +++ b/src/runtime/cgo/gcc_amd64.S @@ -0,0 +1,55 @@ +// 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. + +.file "gcc_amd64.S" + +/* + * Apple still insists on underscore prefixes for C function names. + */ +#if defined(__APPLE__) +#define EXT(s) _##s +#else +#define EXT(s) s +#endif + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard x86-64 ABI, where %rbx, %rbp, %r12-%r15 + * are callee-save so they must be saved explicitly. + * The standard x86-64 ABI passes the three arguments m, g, fn + * in %rdi, %rsi, %rdx. + */ +.globl EXT(crosscall1) +EXT(crosscall1): + pushq %rbx + pushq %rbp + pushq %r12 + pushq %r13 + pushq %r14 + pushq %r15 + +#if defined(_WIN64) + movq %r8, %rdi /* arg of setg_gcc */ + call *%rdx /* setg_gcc */ + call *%rcx /* fn */ +#else + movq %rdi, %rbx + movq %rdx, %rdi /* arg of setg_gcc */ + call *%rsi /* setg_gcc */ + call *%rbx /* fn */ +#endif + + popq %r15 + popq %r14 + popq %r13 + popq %r12 + popq %rbp + popq %rbx + ret + +#ifdef __ELF__ +.section .note.GNU-stack,"",@progbits +#endif diff --git a/src/runtime/cgo/gcc_android.c b/src/runtime/cgo/gcc_android.c new file mode 100644 index 0000000..7ea2135 --- /dev/null +++ b/src/runtime/cgo/gcc_android.c @@ -0,0 +1,90 @@ +// 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 <stdarg.h> +#include <android/log.h> +#include <pthread.h> +#include <dlfcn.h> +#include "libcgo.h" + +void +fatalf(const char* format, ...) +{ + va_list ap; + + // Write to both stderr and logcat. + // + // When running from an .apk, /dev/stderr and /dev/stdout + // redirect to /dev/null. And when running a test binary + // via adb shell, it's easy to miss logcat. + + fprintf(stderr, "runtime/cgo: "); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + + va_start(ap, format); + __android_log_vprint(ANDROID_LOG_FATAL, "runtime/cgo", format, ap); + va_end(ap); + + abort(); +} + +// Truncated to a different magic value on 32-bit; that's ok. +#define magic1 (0x23581321345589ULL) + +// From https://android.googlesource.com/platform/bionic/+/refs/heads/android10-tests-release/libc/private/bionic_asm_tls.h#69. +#define TLS_SLOT_APP 2 + +// inittls allocates a thread-local storage slot for g. +// +// It finds the first available slot using pthread_key_create and uses +// it as the offset value for runtime.tls_g. +static void +inittls(void **tlsg, void **tlsbase) +{ + pthread_key_t k; + int i, err; + void *handle, *get_ver, *off; + + // Check for Android Q where we can use the free TLS_SLOT_APP slot. + handle = dlopen("libc.so", RTLD_LAZY); + if (handle == NULL) { + fatalf("inittls: failed to dlopen main program"); + return; + } + // android_get_device_api_level is introduced in Android Q, so its mere presence + // is enough. + get_ver = dlsym(handle, "android_get_device_api_level"); + dlclose(handle); + if (get_ver != NULL) { + off = (void *)(TLS_SLOT_APP*sizeof(void *)); + // tlsg is initialized to Q's free TLS slot. Verify it while we're here. + if (*tlsg != off) { + fatalf("tlsg offset wrong, got %ld want %ld\n", *tlsg, off); + } + return; + } + + err = pthread_key_create(&k, nil); + if(err != 0) { + fatalf("pthread_key_create failed: %d", err); + } + pthread_setspecific(k, (void*)magic1); + // If thread local slots are laid out as we expect, our magic word will + // be located at some low offset from tlsbase. However, just in case something went + // wrong, the search is limited to sensible offsets. PTHREAD_KEYS_MAX was the + // original limit, but issue 19472 made a higher limit necessary. + for (i=0; i<384; i++) { + if (*(tlsbase+i) == (void*)magic1) { + *tlsg = (void*)(i*sizeof(void *)); + pthread_setspecific(k, 0); + return; + } + } + fatalf("inittls: could not find pthread key"); +} + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase) = inittls; diff --git a/src/runtime/cgo/gcc_arm.S b/src/runtime/cgo/gcc_arm.S new file mode 100644 index 0000000..3df8143 --- /dev/null +++ b/src/runtime/cgo/gcc_arm.S @@ -0,0 +1,31 @@ +// 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. + +.file "gcc_arm.S" + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard ARM EABI, where r4-r11 are callee-save, so they + * must be saved explicitly. + */ +.globl crosscall1 +crosscall1: + push {r4, r5, r6, r7, r8, r9, r10, r11, ip, lr} + mov r4, r0 + mov r5, r1 + mov r0, r2 + + // Because the assembler might target an earlier revision of the ISA + // by default, we encode BLX as a .word. + .word 0xe12fff35 // blx r5 // setg(g) + .word 0xe12fff34 // blx r4 // fn() + + pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc} + + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_arm64.S b/src/runtime/cgo/gcc_arm64.S new file mode 100644 index 0000000..865f67c --- /dev/null +++ b/src/runtime/cgo/gcc_arm64.S @@ -0,0 +1,84 @@ +// 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. + +.file "gcc_arm64.S" + +/* + * Apple still insists on underscore prefixes for C function names. + */ +#if defined(__APPLE__) +#define EXT(s) _##s +#else +#define EXT(s) s +#endif + +// Apple's ld64 wants 4-byte alignment for ARM code sections. +// .align in both Apple as and GNU as treat n as aligning to 2**n bytes. +.align 2 + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard ARM EABI, where x19-x29 are callee-save, so they + * must be saved explicitly, along with x30 (LR). + */ +.globl EXT(crosscall1) +EXT(crosscall1): + .cfi_startproc + stp x29, x30, [sp, #-96]! + .cfi_def_cfa_offset 96 + .cfi_offset 29, -96 + .cfi_offset 30, -88 + mov x29, sp + .cfi_def_cfa_register 29 + stp x19, x20, [sp, #80] + .cfi_offset 19, -16 + .cfi_offset 20, -8 + stp x21, x22, [sp, #64] + .cfi_offset 21, -32 + .cfi_offset 22, -24 + stp x23, x24, [sp, #48] + .cfi_offset 23, -48 + .cfi_offset 24, -40 + stp x25, x26, [sp, #32] + .cfi_offset 25, -64 + .cfi_offset 26, -56 + stp x27, x28, [sp, #16] + .cfi_offset 27, -80 + .cfi_offset 28, -72 + + mov x19, x0 + mov x20, x1 + mov x0, x2 + + blr x20 + blr x19 + + ldp x27, x28, [sp, #16] + .cfi_restore 27 + .cfi_restore 28 + ldp x25, x26, [sp, #32] + .cfi_restore 25 + .cfi_restore 26 + ldp x23, x24, [sp, #48] + .cfi_restore 23 + .cfi_restore 24 + ldp x21, x22, [sp, #64] + .cfi_restore 21 + .cfi_restore 22 + ldp x19, x20, [sp, #80] + .cfi_restore 19 + .cfi_restore 20 + ldp x29, x30, [sp], #96 + .cfi_restore 29 + .cfi_restore 30 + .cfi_def_cfa 31, 0 + ret + .cfi_endproc + + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_context.c b/src/runtime/cgo/gcc_context.c new file mode 100644 index 0000000..ad58692 --- /dev/null +++ b/src/runtime/cgo/gcc_context.c @@ -0,0 +1,20 @@ +// 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 unix || windows + +#include "libcgo.h" + +// Releases the cgo traceback context. +void _cgo_release_context(uintptr_t ctxt) { + void (*pfn)(struct context_arg*); + + pfn = _cgo_get_context_function(); + if (ctxt != 0 && pfn != nil) { + struct context_arg arg; + + arg.Context = ctxt; + (*pfn)(&arg); + } +} diff --git a/src/runtime/cgo/gcc_darwin_amd64.c b/src/runtime/cgo/gcc_darwin_amd64.c new file mode 100644 index 0000000..5b5e369 --- /dev/null +++ b/src/runtime/cgo/gcc_darwin_amd64.c @@ -0,0 +1,60 @@ +// 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 <string.h> /* for strerror */ +#include <pthread.h> +#include <signal.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + size = pthread_get_stacksize_np(pthread_self()); + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_darwin_arm64.c b/src/runtime/cgo/gcc_darwin_arm64.c new file mode 100644 index 0000000..f1344de --- /dev/null +++ b/src/runtime/cgo/gcc_darwin_arm64.c @@ -0,0 +1,139 @@ +// 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 <limits.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> /* for strerror */ +#include <sys/param.h> +#include <unistd.h> +#include <stdlib.h> + +#include "libcgo.h" +#include "libcgo_unix.h" + +#include <TargetConditionals.h> + +#if TARGET_OS_IPHONE +#include <CoreFoundation/CFBundle.h> +#include <CoreFoundation/CFString.h> +#endif + +static void *threadentry(void*); +static void (*setg_gcc)(void*); + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + //fprintf(stderr, "runtime/cgo: _cgo_sys_thread_start: fn=%p, g=%p\n", ts->fn, ts->g); // debug + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + size = pthread_get_stacksize_np(pthread_self()); + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %s\n", strerror(err)); + abort(); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + +#if TARGET_OS_IPHONE + darwin_arm_init_thread_exception_port(); +#endif + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} + +#if TARGET_OS_IPHONE + +// init_working_dir sets the current working directory to the app root. +// By default ios/arm64 processes start in "/". +static void +init_working_dir() +{ + CFBundleRef bundle = CFBundleGetMainBundle(); + if (bundle == NULL) { + fprintf(stderr, "runtime/cgo: no main bundle\n"); + return; + } + CFURLRef url_ref = CFBundleCopyResourceURL(bundle, CFSTR("Info"), CFSTR("plist"), NULL); + if (url_ref == NULL) { + // No Info.plist found. It can happen on Corellium virtual devices. + return; + } + CFStringRef url_str_ref = CFURLGetString(url_ref); + char buf[MAXPATHLEN]; + Boolean res = CFStringGetCString(url_str_ref, buf, sizeof(buf), kCFStringEncodingUTF8); + CFRelease(url_ref); + if (!res) { + fprintf(stderr, "runtime/cgo: cannot get URL string\n"); + return; + } + + // url is of the form "file:///path/to/Info.plist". + // strip it down to the working directory "/path/to". + int url_len = strlen(buf); + if (url_len < sizeof("file://")+sizeof("/Info.plist")) { + fprintf(stderr, "runtime/cgo: bad URL: %s\n", buf); + return; + } + buf[url_len-sizeof("/Info.plist")+1] = 0; + char *dir = &buf[0] + sizeof("file://")-1; + + if (chdir(dir) != 0) { + fprintf(stderr, "runtime/cgo: chdir(%s) failed\n", dir); + } + + // The test harness in go_ios_exec passes the relative working directory + // in the GoExecWrapperWorkingDirectory property of the app bundle. + CFStringRef wd_ref = CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("GoExecWrapperWorkingDirectory")); + if (wd_ref != NULL) { + if (!CFStringGetCString(wd_ref, buf, sizeof(buf), kCFStringEncodingUTF8)) { + fprintf(stderr, "runtime/cgo: cannot get GoExecWrapperWorkingDirectory string\n"); + return; + } + if (chdir(buf) != 0) { + fprintf(stderr, "runtime/cgo: chdir(%s) failed\n", buf); + } + } +} + +#endif // TARGET_OS_IPHONE + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + //fprintf(stderr, "x_cgo_init = %p\n", &x_cgo_init); // aid debugging in presence of ASLR + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); + +#if TARGET_OS_IPHONE + darwin_arm_init_mach_exception_handler(); + darwin_arm_init_thread_exception_port(); + init_working_dir(); +#endif +} diff --git a/src/runtime/cgo/gcc_dragonfly_amd64.c b/src/runtime/cgo/gcc_dragonfly_amd64.c new file mode 100644 index 0000000..009d4b4 --- /dev/null +++ b/src/runtime/cgo/gcc_dragonfly_amd64.c @@ -0,0 +1,60 @@ +// 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 <sys/types.h> +#include <sys/signalvar.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + SIGFILLSET(ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_fatalf.c b/src/runtime/cgo/gcc_fatalf.c new file mode 100644 index 0000000..822c015 --- /dev/null +++ b/src/runtime/cgo/gcc_fatalf.c @@ -0,0 +1,23 @@ +// 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 aix || (!android && linux) || dragonfly || freebsd || netbsd || openbsd || solaris + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include "libcgo.h" + +void +fatalf(const char* format, ...) +{ + va_list ap; + + fprintf(stderr, "runtime/cgo: "); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); + fprintf(stderr, "\n"); + abort(); +} diff --git a/src/runtime/cgo/gcc_freebsd.c b/src/runtime/cgo/gcc_freebsd.c new file mode 100644 index 0000000..a941211 --- /dev/null +++ b/src/runtime/cgo/gcc_freebsd.c @@ -0,0 +1,71 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build freebsd && (386 || arm || arm64 || riscv64) + +#include <sys/types.h> +#include <sys/signalvar.h> +#include <machine/sysarch.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +#ifdef ARM_TP_ADDRESS +// ARM_TP_ADDRESS is (ARM_VECTORS_HIGH + 0x1000) or 0xffff1000 +// and is known to runtime.read_tls_fallback. Verify it with +// cpp. +#if ARM_TP_ADDRESS != 0xffff1000 +#error Wrong ARM_TP_ADDRESS! +#endif +#endif + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + SIGFILLSET(ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_freebsd_amd64.c b/src/runtime/cgo/gcc_freebsd_amd64.c new file mode 100644 index 0000000..31905f2 --- /dev/null +++ b/src/runtime/cgo/gcc_freebsd_amd64.c @@ -0,0 +1,71 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <sys/types.h> +#include <errno.h> +#include <sys/signalvar.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + uintptr *pbounds; + + // Deal with memory sanitizer/clang interaction. + // See gcc_linux_amd64.c for details. + setg_gcc = setg; + pbounds = (uintptr*)malloc(2 * sizeof(uintptr)); + if (pbounds == NULL) { + fatalf("malloc failed: %s", strerror(errno)); + } + _cgo_set_stacklo(g, pbounds); + free(pbounds); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + SIGFILLSET(ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + _cgo_tsan_acquire(); + free(v); + _cgo_tsan_release(); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_freebsd_sigaction.c b/src/runtime/cgo/gcc_freebsd_sigaction.c new file mode 100644 index 0000000..b324983 --- /dev/null +++ b/src/runtime/cgo/gcc_freebsd_sigaction.c @@ -0,0 +1,80 @@ +// 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 freebsd && amd64 + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <signal.h> + +#include "libcgo.h" + +// go_sigaction_t is a C version of the sigactiont struct from +// os_freebsd.go. This definition — and its conversion to and from struct +// sigaction — are specific to freebsd/amd64. +typedef struct { + uint32_t __bits[_SIG_WORDS]; +} go_sigset_t; +typedef struct { + uintptr_t handler; + int32_t flags; + go_sigset_t mask; +} go_sigaction_t; + +int32_t +x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *oldgoact) { + int32_t ret; + struct sigaction act; + struct sigaction oldact; + size_t i; + + _cgo_tsan_acquire(); + + memset(&act, 0, sizeof act); + memset(&oldact, 0, sizeof oldact); + + if (goact) { + if (goact->flags & SA_SIGINFO) { + act.sa_sigaction = (void(*)(int, siginfo_t*, void*))(goact->handler); + } else { + act.sa_handler = (void(*)(int))(goact->handler); + } + sigemptyset(&act.sa_mask); + for (i = 0; i < 8 * sizeof(goact->mask); i++) { + if (goact->mask.__bits[i/32] & ((uint32_t)(1)<<(i&31))) { + sigaddset(&act.sa_mask, i+1); + } + } + act.sa_flags = goact->flags; + } + + ret = sigaction(signum, goact ? &act : NULL, oldgoact ? &oldact : NULL); + if (ret == -1) { + // runtime.sigaction expects _cgo_sigaction to return errno on error. + _cgo_tsan_release(); + return errno; + } + + if (oldgoact) { + if (oldact.sa_flags & SA_SIGINFO) { + oldgoact->handler = (uintptr_t)(oldact.sa_sigaction); + } else { + oldgoact->handler = (uintptr_t)(oldact.sa_handler); + } + for (i = 0 ; i < _SIG_WORDS; i++) { + oldgoact->mask.__bits[i] = 0; + } + for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) { + if (sigismember(&oldact.sa_mask, i+1) == 1) { + oldgoact->mask.__bits[i/32] |= (uint32_t)(1)<<(i&31); + } + } + oldgoact->flags = oldact.sa_flags; + } + + _cgo_tsan_release(); + return ret; +} diff --git a/src/runtime/cgo/gcc_libinit.c b/src/runtime/cgo/gcc_libinit.c new file mode 100644 index 0000000..68f4a02 --- /dev/null +++ b/src/runtime/cgo/gcc_libinit.c @@ -0,0 +1,171 @@ +// 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 + +#include <pthread.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> // strerror +#include <time.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static pthread_cond_t runtime_init_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t runtime_init_mu = PTHREAD_MUTEX_INITIALIZER; +static int runtime_init_done; + +// pthread_g is a pthread specific key, for storing the g that binded to the C thread. +// The registered pthread_key_destructor will dropm, when the pthread-specified value g is not NULL, +// while a C thread is exiting. +static pthread_key_t pthread_g; +static void pthread_key_destructor(void* g); +uintptr_t x_cgo_pthread_key_created; +void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t); + +// The context function, used when tracing back C calls into Go. +static void (*cgo_context_function)(struct context_arg*); + +void +x_cgo_sys_thread_create(void* (*func)(void*), void* arg) { + pthread_t p; + int err = _cgo_try_pthread_create(&p, NULL, func, arg); + if (err != 0) { + fprintf(stderr, "pthread_create failed: %s", strerror(err)); + abort(); + } +} + +uintptr_t +_cgo_wait_runtime_init_done(void) { + void (*pfn)(struct context_arg*); + pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); + + int done = 2; + if (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) != done) { + pthread_mutex_lock(&runtime_init_mu); + while (__atomic_load_n(&runtime_init_done, __ATOMIC_CONSUME) == 0) { + pthread_cond_wait(&runtime_init_cond, &runtime_init_mu); + } + + // The key and x_cgo_pthread_key_created are for the whole program, + // whereas the specific and destructor is per thread. + if (x_cgo_pthread_key_created == 0 && pthread_key_create(&pthread_g, pthread_key_destructor) == 0) { + x_cgo_pthread_key_created = 1; + } + + + // TODO(iant): For the case of a new C thread calling into Go, such + // as when using -buildmode=c-archive, we know that Go runtime + // initialization is complete but we do not know that all Go init + // functions have been run. We should not fetch cgo_context_function + // until they have been, because that is where a call to + // SetCgoTraceback is likely to occur. We are going to wait for Go + // initialization to be complete anyhow, later, by waiting for + // main_init_done to be closed in cgocallbackg1. We should wait here + // instead. See also issue #15943. + pfn = __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); + + __atomic_store_n(&runtime_init_done, done, __ATOMIC_RELEASE); + pthread_mutex_unlock(&runtime_init_mu); + } + + if (pfn != nil) { + struct context_arg arg; + + arg.Context = 0; + (*pfn)(&arg); + return arg.Context; + } + return 0; +} + +// _cgo_set_stacklo sets g->stacklo based on the stack size. +// This is common code called from x_cgo_init, which is itself +// called by rt0_go in the runtime package. +void _cgo_set_stacklo(G *g, uintptr *pbounds) +{ + uintptr bounds[2]; + + // pbounds can be passed in by the caller; see gcc_linux_amd64.c. + if (pbounds == NULL) { + pbounds = &bounds[0]; + } + + x_cgo_getstackbound(pbounds); + + g->stacklo = *pbounds; + + // Sanity check the results now, rather than getting a + // morestack on g0 crash. + if (g->stacklo >= g->stackhi) { + fprintf(stderr, "runtime/cgo: bad stack bounds: lo=%p hi=%p\n", (void*)(g->stacklo), (void*)(g->stackhi)); + abort(); + } +} + +// Store the g into a thread-specific value associated with the pthread key pthread_g. +// And pthread_key_destructor will dropm when the thread is exiting. +void x_cgo_bindm(void* g) { + // We assume this will always succeed, otherwise, there might be extra M leaking, + // when a C thread exits after a cgo call. + // We only invoke this function once per thread in runtime.needAndBindM, + // and the next calls just reuse the bound m. + pthread_setspecific(pthread_g, g); +} + +void +x_cgo_notify_runtime_init_done(void* dummy __attribute__ ((unused))) { + pthread_mutex_lock(&runtime_init_mu); + __atomic_store_n(&runtime_init_done, 1, __ATOMIC_RELEASE); + pthread_cond_broadcast(&runtime_init_cond); + pthread_mutex_unlock(&runtime_init_mu); +} + +// Sets the context function to call to record the traceback context +// when calling a Go function from C code. Called from runtime.SetCgoTraceback. +void x_cgo_set_context_function(void (*context)(struct context_arg*)) { + __atomic_store_n(&cgo_context_function, context, __ATOMIC_RELEASE); +} + +// Gets the context function. +void (*(_cgo_get_context_function(void)))(struct context_arg*) { + return __atomic_load_n(&cgo_context_function, __ATOMIC_CONSUME); +} + +// _cgo_try_pthread_create retries pthread_create if it fails with +// EAGAIN. +int +_cgo_try_pthread_create(pthread_t* thread, const pthread_attr_t* attr, void* (*pfn)(void*), void* arg) { + int tries; + int err; + struct timespec ts; + + for (tries = 0; tries < 20; tries++) { + err = pthread_create(thread, attr, pfn, arg); + if (err == 0) { + pthread_detach(*thread); + return 0; + } + if (err != EAGAIN) { + return err; + } + ts.tv_sec = 0; + ts.tv_nsec = (tries + 1) * 1000 * 1000; // Milliseconds. + nanosleep(&ts, nil); + } + return EAGAIN; +} + +static void +pthread_key_destructor(void* g) { + if (x_crosscall2_ptr != NULL) { + // fn == NULL means dropm. + // We restore g by using the stored g, before dropm in runtime.cgocallback, + // since the g stored in the TLS by Go might be cleared in some platforms, + // before this destructor invoked. + x_crosscall2_ptr(NULL, g, 0, 0); + } +} diff --git a/src/runtime/cgo/gcc_libinit_windows.c b/src/runtime/cgo/gcc_libinit_windows.c new file mode 100644 index 0000000..9a8c65e --- /dev/null +++ b/src/runtime/cgo/gcc_libinit_windows.c @@ -0,0 +1,158 @@ +// 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. + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +#include "libcgo.h" +#include "libcgo_windows.h" + +// Ensure there's one symbol marked __declspec(dllexport). +// If there are no exported symbols, the unfortunate behavior of +// the binutils linker is to also strip the relocations table, +// resulting in non-PIE binary. The other option is the +// --export-all-symbols flag, but we don't need to export all symbols +// and this may overflow the export table (#40795). +// See https://sourceware.org/bugzilla/show_bug.cgi?id=19011 +__declspec(dllexport) int _cgo_dummy_export; + +static volatile LONG runtime_init_once_gate = 0; +static volatile LONG runtime_init_once_done = 0; + +static CRITICAL_SECTION runtime_init_cs; + +static HANDLE runtime_init_wait; +static int runtime_init_done; + +uintptr_t x_cgo_pthread_key_created; +void (*x_crosscall2_ptr)(void (*fn)(void *), void *, int, size_t); + +// Pre-initialize the runtime synchronization objects +void +_cgo_preinit_init() { + runtime_init_wait = CreateEvent(NULL, TRUE, FALSE, NULL); + if (runtime_init_wait == NULL) { + fprintf(stderr, "runtime: failed to create runtime initialization wait event.\n"); + abort(); + } + + InitializeCriticalSection(&runtime_init_cs); +} + +// Make sure that the preinit sequence has run. +void +_cgo_maybe_run_preinit() { + if (!InterlockedExchangeAdd(&runtime_init_once_done, 0)) { + if (InterlockedIncrement(&runtime_init_once_gate) == 1) { + _cgo_preinit_init(); + InterlockedIncrement(&runtime_init_once_done); + } else { + // Decrement to avoid overflow. + InterlockedDecrement(&runtime_init_once_gate); + while(!InterlockedExchangeAdd(&runtime_init_once_done, 0)) { + Sleep(0); + } + } + } +} + +void +x_cgo_sys_thread_create(void (*func)(void*), void* arg) { + _cgo_beginthread(func, arg); +} + +int +_cgo_is_runtime_initialized() { + EnterCriticalSection(&runtime_init_cs); + int status = runtime_init_done; + LeaveCriticalSection(&runtime_init_cs); + return status; +} + +uintptr_t +_cgo_wait_runtime_init_done(void) { + void (*pfn)(struct context_arg*); + + _cgo_maybe_run_preinit(); + while (!_cgo_is_runtime_initialized()) { + WaitForSingleObject(runtime_init_wait, INFINITE); + } + pfn = _cgo_get_context_function(); + if (pfn != nil) { + struct context_arg arg; + + arg.Context = 0; + (*pfn)(&arg); + return arg.Context; + } + return 0; +} + +// Should not be used since x_cgo_pthread_key_created will always be zero. +void x_cgo_bindm(void* dummy) { + fprintf(stderr, "unexpected cgo_bindm on Windows\n"); + abort(); +} + +void +x_cgo_notify_runtime_init_done(void* dummy) { + _cgo_maybe_run_preinit(); + + EnterCriticalSection(&runtime_init_cs); + runtime_init_done = 1; + LeaveCriticalSection(&runtime_init_cs); + + if (!SetEvent(runtime_init_wait)) { + fprintf(stderr, "runtime: failed to signal runtime initialization complete.\n"); + abort(); + } +} + +// The context function, used when tracing back C calls into Go. +static void (*cgo_context_function)(struct context_arg*); + +// Sets the context function to call to record the traceback context +// when calling a Go function from C code. Called from runtime.SetCgoTraceback. +void x_cgo_set_context_function(void (*context)(struct context_arg*)) { + EnterCriticalSection(&runtime_init_cs); + cgo_context_function = context; + LeaveCriticalSection(&runtime_init_cs); +} + +// Gets the context function. +void (*(_cgo_get_context_function(void)))(struct context_arg*) { + void (*ret)(struct context_arg*); + + EnterCriticalSection(&runtime_init_cs); + ret = cgo_context_function; + LeaveCriticalSection(&runtime_init_cs); + return ret; +} + +void _cgo_beginthread(void (*func)(void*), void* arg) { + int tries; + uintptr_t thandle; + + for (tries = 0; tries < 20; tries++) { + thandle = _beginthread(func, 0, arg); + if (thandle == -1 && errno == EACCES) { + // "Insufficient resources", try again in a bit. + // + // Note that the first Sleep(0) is a yield. + Sleep(tries); // milliseconds + continue; + } else if (thandle == -1) { + break; + } + return; // Success! + } + + fprintf(stderr, "runtime: failed to create new OS thread (%d)\n", errno); + abort(); +} diff --git a/src/runtime/cgo/gcc_linux.c b/src/runtime/cgo/gcc_linux.c new file mode 100644 index 0000000..9624df5 --- /dev/null +++ b/src/runtime/cgo/gcc_linux.c @@ -0,0 +1,66 @@ +// 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 linux && (386 || arm || loong64 || mips || mipsle || mips64 || mips64le || riscv64) + +#include <pthread.h> +#include <string.h> +#include <signal.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((common)); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + setg_gcc = setg; + + _cgo_set_stacklo(g, NULL); + + if (x_cgo_inittls) { + x_cgo_inittls(tlsg, tlsbase); + } +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_linux_amd64.c b/src/runtime/cgo/gcc_linux_amd64.c new file mode 100644 index 0000000..dcb596e --- /dev/null +++ b/src/runtime/cgo/gcc_linux_amd64.c @@ -0,0 +1,91 @@ +// 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 <pthread.h> +#include <errno.h> +#include <string.h> // strerror +#include <signal.h> +#include <stdlib.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +// This will be set in gcc_android.c for android-specific customization. +void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((common)); + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + uintptr *pbounds; + + /* The memory sanitizer distributed with versions of clang + before 3.8 has a bug: if you call mmap before malloc, mmap + may return an address that is later overwritten by the msan + library. Avoid this problem by forcing a call to malloc + here, before we ever call malloc. + + This is only required for the memory sanitizer, so it's + unfortunate that we always run it. It should be possible + to remove this when we no longer care about versions of + clang before 3.8. The test for this is + misc/cgo/testsanitizers. + + GCC works hard to eliminate a seemingly unnecessary call to + malloc, so we actually use the memory we allocate. */ + + setg_gcc = setg; + pbounds = (uintptr*)malloc(2 * sizeof(uintptr)); + if (pbounds == NULL) { + fatalf("malloc failed: %s", strerror(errno)); + } + _cgo_set_stacklo(g, pbounds); + free(pbounds); + + if (x_cgo_inittls) { + x_cgo_inittls(tlsg, tlsbase); + } +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + _cgo_tsan_acquire(); + free(v); + _cgo_tsan_release(); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_linux_arm64.c b/src/runtime/cgo/gcc_linux_arm64.c new file mode 100644 index 0000000..0dcff2c --- /dev/null +++ b/src/runtime/cgo/gcc_linux_arm64.c @@ -0,0 +1,87 @@ +// 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 <pthread.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase) __attribute__((common)); +static void (*setg_gcc)(void*); + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + uintptr *pbounds; + + /* The memory sanitizer distributed with versions of clang + before 3.8 has a bug: if you call mmap before malloc, mmap + may return an address that is later overwritten by the msan + library. Avoid this problem by forcing a call to malloc + here, before we ever call malloc. + + This is only required for the memory sanitizer, so it's + unfortunate that we always run it. It should be possible + to remove this when we no longer care about versions of + clang before 3.8. The test for this is + misc/cgo/testsanitizers. + + GCC works hard to eliminate a seemingly unnecessary call to + malloc, so we actually use the memory we allocate. */ + + setg_gcc = setg; + pbounds = (uintptr*)malloc(2 * sizeof(uintptr)); + if (pbounds == NULL) { + fatalf("malloc failed: %s", strerror(errno)); + } + _cgo_set_stacklo(g, pbounds); + free(pbounds); + + if (x_cgo_inittls) { + x_cgo_inittls(tlsg, tlsbase); + } +} diff --git a/src/runtime/cgo/gcc_linux_ppc64x.S b/src/runtime/cgo/gcc_linux_ppc64x.S new file mode 100644 index 0000000..745d232 --- /dev/null +++ b/src/runtime/cgo/gcc_linux_ppc64x.S @@ -0,0 +1,86 @@ +// 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) + +.file "gcc_linux_ppc64x.S" + +// Define a frame which has no argument space, but is compatible with +// a call into a Go ABI. We allocate 32B to match FIXED_FRAME with +// similar semantics, except we store the backchain pointer, not the +// LR at offset 0. R2 is stored in the Go TOC save slot (offset 24). +.set GPR_OFFSET, 32 +.set FPR_OFFSET, GPR_OFFSET + 18*8 +.set VR_OFFSET, FPR_OFFSET + 18*8 +.set FRAME_SIZE, VR_OFFSET + 12*16 + +.macro FOR_EACH_GPR opcode r=14 +.ifge 31 - \r + \opcode \r, GPR_OFFSET + 8*(\r-14)(1) + FOR_EACH_GPR \opcode "(\r+1)" +.endif +.endm + +.macro FOR_EACH_FPR opcode fr=14 +.ifge 31 - \fr + \opcode \fr, FPR_OFFSET + 8*(\fr-14)(1) + FOR_EACH_FPR \opcode "(\fr+1)" +.endif +.endm + +.macro FOR_EACH_VR opcode vr=20 +.ifge 31 - \vr + li 0, VR_OFFSET + 16*(\vr-20) + \opcode \vr, 1, 0 + FOR_EACH_VR \opcode "(\vr+1)" +.endif +.endm + +/* + * void crosscall_ppc64(void (*fn)(void), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard ppc64 C ABI, where r2, r14-r31, f14-f31 are + * callee-save, so they must be saved explicitly. + */ +.globl crosscall_ppc64 +crosscall_ppc64: + // Start with standard C stack frame layout and linkage + mflr %r0 + std %r0, 16(%r1) // Save LR in caller's frame + mfcr %r0 + std %r0, 8(%r1) // Save CR in caller's frame + stdu %r1, -FRAME_SIZE(%r1) + std %r2, 24(%r1) + + FOR_EACH_GPR std + FOR_EACH_FPR stfd + FOR_EACH_VR stvx + + // Set up Go ABI constant registers + li %r0, 0 + + // Restore g pointer (r30 in Go ABI, which may have been clobbered by C) + mr %r30, %r4 + + // Call fn + mr %r12, %r3 + mtctr %r3 + bctrl + + FOR_EACH_GPR ld + FOR_EACH_FPR lfd + FOR_EACH_VR lvx + + ld %r2, 24(%r1) + addi %r1, %r1, FRAME_SIZE + ld %r0, 16(%r1) + mtlr %r0 + ld %r0, 8(%r1) + mtcr %r0 + blr + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_linux_s390x.c b/src/runtime/cgo/gcc_linux_s390x.c new file mode 100644 index 0000000..4b9f76c --- /dev/null +++ b/src/runtime/cgo/gcc_linux_s390x.c @@ -0,0 +1,63 @@ +// 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 <pthread.h> +#include <string.h> +#include <signal.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsbase) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall_s390x(void (*fn)(void), void *g); + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + // Save g for this thread in C TLS + setg_gcc((void*)ts.g); + + crosscall_s390x(ts.fn, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_loong64.S b/src/runtime/cgo/gcc_loong64.S new file mode 100644 index 0000000..6b7668f --- /dev/null +++ b/src/runtime/cgo/gcc_loong64.S @@ -0,0 +1,67 @@ +// 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. + +.file "gcc_loong64.S" + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard lp64d ABI, where $r1, $r3, $r23-$r30, and $f24-$f31 + * are callee-save, so they must be saved explicitly, along with $r1 (LR). + */ +.globl crosscall1 +crosscall1: + addi.d $r3, $r3, -160 + st.d $r1, $r3, 0 + st.d $r23, $r3, 8 + st.d $r24, $r3, 16 + st.d $r25, $r3, 24 + st.d $r26, $r3, 32 + st.d $r27, $r3, 40 + st.d $r28, $r3, 48 + st.d $r29, $r3, 56 + st.d $r30, $r3, 64 + st.d $r2, $r3, 72 + st.d $r22, $r3, 80 + fst.d $f24, $r3, 88 + fst.d $f25, $r3, 96 + fst.d $f26, $r3, 104 + fst.d $f27, $r3, 112 + fst.d $f28, $r3, 120 + fst.d $f29, $r3, 128 + fst.d $f30, $r3, 136 + fst.d $f31, $r3, 144 + + move $r18, $r4 // save R4 + move $r19, $r6 + jirl $r1, $r5, 0 // call setg_gcc (clobbers R4) + jirl $r1, $r18, 0 // call fn + + ld.d $r23, $r3, 8 + ld.d $r24, $r3, 16 + ld.d $r25, $r3, 24 + ld.d $r26, $r3, 32 + ld.d $r27, $r3, 40 + ld.d $r28, $r3, 48 + ld.d $r29, $r3, 56 + ld.d $r30, $r3, 64 + ld.d $r2, $r3, 72 + ld.d $r22, $r3, 80 + fld.d $f24, $r3, 88 + fld.d $f25, $r3, 96 + fld.d $f26, $r3, 104 + fld.d $f27, $r3, 112 + fld.d $f28, $r3, 120 + fld.d $f29, $r3, 128 + fld.d $f30, $r3, 136 + fld.d $f31, $r3, 144 + ld.d $r1, $r3, 0 + addi.d $r3, $r3, 160 + jirl $r0, $r1, 0 + + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_mips64x.S b/src/runtime/cgo/gcc_mips64x.S new file mode 100644 index 0000000..1629e47 --- /dev/null +++ b/src/runtime/cgo/gcc_mips64x.S @@ -0,0 +1,89 @@ +// 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 mips64 || mips64le + +.file "gcc_mips64x.S" + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard MIPS N64 ABI, where $16-$23, $28, $30, and $f24-$f31 + * are callee-save, so they must be saved explicitly, along with $31 (LR). + */ +.globl crosscall1 +.set noat +crosscall1: +#ifndef __mips_soft_float + daddiu $29, $29, -160 +#else + daddiu $29, $29, -96 // For soft-float, no need to make room for FP registers +#endif + sd $31, 0($29) + sd $16, 8($29) + sd $17, 16($29) + sd $18, 24($29) + sd $19, 32($29) + sd $20, 40($29) + sd $21, 48($29) + sd $22, 56($29) + sd $23, 64($29) + sd $28, 72($29) + sd $30, 80($29) +#ifndef __mips_soft_float + sdc1 $f24, 88($29) + sdc1 $f25, 96($29) + sdc1 $f26, 104($29) + sdc1 $f27, 112($29) + sdc1 $f28, 120($29) + sdc1 $f29, 128($29) + sdc1 $f30, 136($29) + sdc1 $f31, 144($29) +#endif + + // prepare SB register = pc & 0xffffffff00000000 + bal 1f +1: + dsrl $28, $31, 32 + dsll $28, $28, 32 + + move $20, $4 // save R4 + move $1, $6 + jalr $5 // call setg_gcc (clobbers R4) + jalr $20 // call fn + + ld $16, 8($29) + ld $17, 16($29) + ld $18, 24($29) + ld $19, 32($29) + ld $20, 40($29) + ld $21, 48($29) + ld $22, 56($29) + ld $23, 64($29) + ld $28, 72($29) + ld $30, 80($29) +#ifndef __mips_soft_float + ldc1 $f24, 88($29) + ldc1 $f25, 96($29) + ldc1 $f26, 104($29) + ldc1 $f27, 112($29) + ldc1 $f28, 120($29) + ldc1 $f29, 128($29) + ldc1 $f30, 136($29) + ldc1 $f31, 144($29) +#endif + ld $31, 0($29) +#ifndef __mips_soft_float + daddiu $29, $29, 160 +#else + daddiu $29, $29, 96 +#endif + jr $31 + +.set at + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_mipsx.S b/src/runtime/cgo/gcc_mipsx.S new file mode 100644 index 0000000..fb19c11 --- /dev/null +++ b/src/runtime/cgo/gcc_mipsx.S @@ -0,0 +1,77 @@ +// 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 mips || mipsle + +.file "gcc_mipsx.S" + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard MIPS O32 ABI, where $16-$23, $30, and $f20-$f31 + * are callee-save, so they must be saved explicitly, along with $31 (LR). + */ +.globl crosscall1 +.set noat +crosscall1: +#ifndef __mips_soft_float + addiu $29, $29, -88 +#else + addiu $29, $29, -40 // For soft-float, no need to make room for FP registers +#endif + sw $31, 0($29) + sw $16, 4($29) + sw $17, 8($29) + sw $18, 12($29) + sw $19, 16($29) + sw $20, 20($29) + sw $21, 24($29) + sw $22, 28($29) + sw $23, 32($29) + sw $30, 36($29) + +#ifndef __mips_soft_float + sdc1 $f20, 40($29) + sdc1 $f22, 48($29) + sdc1 $f24, 56($29) + sdc1 $f26, 64($29) + sdc1 $f28, 72($29) + sdc1 $f30, 80($29) +#endif + move $20, $4 // save R4 + move $4, $6 + jalr $5 // call setg_gcc + jalr $20 // call fn + + lw $16, 4($29) + lw $17, 8($29) + lw $18, 12($29) + lw $19, 16($29) + lw $20, 20($29) + lw $21, 24($29) + lw $22, 28($29) + lw $23, 32($29) + lw $30, 36($29) +#ifndef __mips_soft_float + ldc1 $f20, 40($29) + ldc1 $f22, 48($29) + ldc1 $f24, 56($29) + ldc1 $f26, 64($29) + ldc1 $f28, 72($29) + ldc1 $f30, 80($29) +#endif + lw $31, 0($29) +#ifndef __mips_soft_float + addiu $29, $29, 88 +#else + addiu $29, $29, 40 +#endif + jr $31 + +.set at + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_mmap.c b/src/runtime/cgo/gcc_mmap.c new file mode 100644 index 0000000..eb710a0 --- /dev/null +++ b/src/runtime/cgo/gcc_mmap.c @@ -0,0 +1,39 @@ +// 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 && (amd64 || arm64 || loong64 || ppc64le)) || (freebsd && amd64) + +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> +#include <sys/mman.h> + +#include "libcgo.h" + +uintptr_t +x_cgo_mmap(void *addr, uintptr_t length, int32_t prot, int32_t flags, int32_t fd, uint32_t offset) { + void *p; + + _cgo_tsan_acquire(); + p = mmap(addr, length, prot, flags, fd, offset); + _cgo_tsan_release(); + if (p == MAP_FAILED) { + /* This is what the Go code expects on failure. */ + return (uintptr_t)errno; + } + return (uintptr_t)p; +} + +void +x_cgo_munmap(void *addr, uintptr_t length) { + int r; + + _cgo_tsan_acquire(); + r = munmap(addr, length); + _cgo_tsan_release(); + if (r < 0) { + /* The Go runtime is not prepared for munmap to fail. */ + abort(); + } +} diff --git a/src/runtime/cgo/gcc_netbsd.c b/src/runtime/cgo/gcc_netbsd.c new file mode 100644 index 0000000..16819ce --- /dev/null +++ b/src/runtime/cgo/gcc_netbsd.c @@ -0,0 +1,73 @@ +// 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 netbsd && (386 || amd64 || arm || arm64) + +#include <sys/types.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + stack_t ss; + + ts = *(ThreadStart*)v; + free(v); + + // On NetBSD, a new thread inherits the signal stack of the + // creating thread. That confuses minit, so we remove that + // signal stack here before calling the regular mstart. It's + // a bit baroque to remove a signal stack here only to add one + // in minit, but it's a simple change that keeps NetBSD + // working like other OS's. At this point all signals are + // blocked, so there is no race. + memset(&ss, 0, sizeof ss); + ss.ss_flags = SS_DISABLE; + sigaltstack(&ss, nil); + + crosscall1(ts.fn, setg_gcc, ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_openbsd.c b/src/runtime/cgo/gcc_openbsd.c new file mode 100644 index 0000000..3a4e545 --- /dev/null +++ b/src/runtime/cgo/gcc_openbsd.c @@ -0,0 +1,61 @@ +// 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 openbsd && (386 || arm || amd64 || arm64 || riscv64) + +#include <sys/types.h> +#include <pthread.h> +#include <signal.h> +#include <string.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_ppc64x.c b/src/runtime/cgo/gcc_ppc64x.c new file mode 100644 index 0000000..98a6549 --- /dev/null +++ b/src/runtime/cgo/gcc_ppc64x.c @@ -0,0 +1,67 @@ +// 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 ppc64 || ppc64le + +#include <pthread.h> +#include <string.h> +#include <signal.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void *threadentry(void*); + +void (*x_cgo_inittls)(void **tlsg, void **tlsbase); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsbase) +{ + setg_gcc = setg; + _cgo_set_stacklo(g, NULL); +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + // Leave stacklo=0 and set stackhi=size; mstart will do the rest. + ts->g->stackhi = size; + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall_ppc64(void (*fn)(void), void *g); + +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + _cgo_tsan_acquire(); + free(v); + _cgo_tsan_release(); + + // Save g for this thread in C TLS + setg_gcc((void*)ts.g); + + crosscall_ppc64(ts.fn, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_riscv64.S b/src/runtime/cgo/gcc_riscv64.S new file mode 100644 index 0000000..8f07649 --- /dev/null +++ b/src/runtime/cgo/gcc_riscv64.S @@ -0,0 +1,82 @@ +// 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. + +.file "gcc_riscv64.S" + +/* + * void crosscall1(void (*fn)(void), void (*setg_gcc)(void *g), void *g) + * + * Calling into the gc tool chain, where all registers are caller save. + * Called from standard RISCV ELF psABI, where x8-x9, x18-x27, f8-f9 and + * f18-f27 are callee-save, so they must be saved explicitly, along with + * x1 (LR). + */ +.globl crosscall1 +crosscall1: + sd x1, -200(sp) + addi sp, sp, -200 + sd x8, 8(sp) + sd x9, 16(sp) + sd x18, 24(sp) + sd x19, 32(sp) + sd x20, 40(sp) + sd x21, 48(sp) + sd x22, 56(sp) + sd x23, 64(sp) + sd x24, 72(sp) + sd x25, 80(sp) + sd x26, 88(sp) + sd x27, 96(sp) + fsd f8, 104(sp) + fsd f9, 112(sp) + fsd f18, 120(sp) + fsd f19, 128(sp) + fsd f20, 136(sp) + fsd f21, 144(sp) + fsd f22, 152(sp) + fsd f23, 160(sp) + fsd f24, 168(sp) + fsd f25, 176(sp) + fsd f26, 184(sp) + fsd f27, 192(sp) + + // a0 = *fn, a1 = *setg_gcc, a2 = *g + mv s1, a0 + mv s0, a1 + mv a0, a2 + jalr ra, s0 // call setg_gcc (clobbers x30 aka g) + jalr ra, s1 // call fn + + ld x1, 0(sp) + ld x8, 8(sp) + ld x9, 16(sp) + ld x18, 24(sp) + ld x19, 32(sp) + ld x20, 40(sp) + ld x21, 48(sp) + ld x22, 56(sp) + ld x23, 64(sp) + ld x24, 72(sp) + ld x25, 80(sp) + ld x26, 88(sp) + ld x27, 96(sp) + fld f8, 104(sp) + fld f9, 112(sp) + fld f18, 120(sp) + fld f19, 128(sp) + fld f20, 136(sp) + fld f21, 144(sp) + fld f22, 152(sp) + fld f23, 160(sp) + fld f24, 168(sp) + fld f25, 176(sp) + fld f26, 184(sp) + fld f27, 192(sp) + addi sp, sp, 200 + + jr ra + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_s390x.S b/src/runtime/cgo/gcc_s390x.S new file mode 100644 index 0000000..8bd30fe --- /dev/null +++ b/src/runtime/cgo/gcc_s390x.S @@ -0,0 +1,58 @@ +// 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. + +.file "gcc_s390x.S" + +/* + * void crosscall_s390x(void (*fn)(void), void *g) + * + * Calling into the go tool chain, where all registers are caller save. + * Called from standard s390x C ABI, where r6-r13, r15, and f8-f15 are + * callee-save, so they must be saved explicitly. + */ +.globl crosscall_s390x +crosscall_s390x: + /* save r6-r15 in the register save area of the calling function */ + stmg %r6, %r15, 48(%r15) + + /* allocate 64 bytes of stack space to save f8-f15 */ + lay %r15, -64(%r15) + + /* save callee-saved floating point registers */ + std %f8, 0(%r15) + std %f9, 8(%r15) + std %f10, 16(%r15) + std %f11, 24(%r15) + std %f12, 32(%r15) + std %f13, 40(%r15) + std %f14, 48(%r15) + std %f15, 56(%r15) + + /* restore g pointer */ + lgr %r13, %r3 + + /* call fn */ + basr %r14, %r2 + + /* restore floating point registers */ + ld %f8, 0(%r15) + ld %f9, 8(%r15) + ld %f10, 16(%r15) + ld %f11, 24(%r15) + ld %f12, 32(%r15) + ld %f13, 40(%r15) + ld %f14, 48(%r15) + ld %f15, 56(%r15) + + /* de-allocate stack frame */ + la %r15, 64(%r15) + + /* restore general purpose registers */ + lmg %r6, %r15, 48(%r15) + + br %r14 /* restored by lmg */ + +#ifdef __ELF__ +.section .note.GNU-stack,"",%progbits +#endif diff --git a/src/runtime/cgo/gcc_setenv.c b/src/runtime/cgo/gcc_setenv.c new file mode 100644 index 0000000..47caa4b --- /dev/null +++ b/src/runtime/cgo/gcc_setenv.c @@ -0,0 +1,27 @@ +// 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 unix + +#include "libcgo.h" + +#include <stdlib.h> + +/* Stub for calling setenv */ +void +x_cgo_setenv(char **arg) +{ + _cgo_tsan_acquire(); + setenv(arg[0], arg[1], 1); + _cgo_tsan_release(); +} + +/* Stub for calling unsetenv */ +void +x_cgo_unsetenv(char **arg) +{ + _cgo_tsan_acquire(); + unsetenv(arg[0]); + _cgo_tsan_release(); +} diff --git a/src/runtime/cgo/gcc_sigaction.c b/src/runtime/cgo/gcc_sigaction.c new file mode 100644 index 0000000..374909b --- /dev/null +++ b/src/runtime/cgo/gcc_sigaction.c @@ -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 linux && (amd64 || arm64 || ppc64le) + +#include <errno.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <signal.h> + +#include "libcgo.h" + +// go_sigaction_t is a C version of the sigactiont struct from +// defs_linux_amd64.go. This definition — and its conversion to and from struct +// sigaction — are specific to linux/amd64. +typedef struct { + uintptr_t handler; + uint64_t flags; + uintptr_t restorer; + uint64_t mask; +} go_sigaction_t; + +// SA_RESTORER is part of the kernel interface. +// This is Linux i386/amd64 specific. +#ifndef SA_RESTORER +#define SA_RESTORER 0x4000000 +#endif + +int32_t +x_cgo_sigaction(intptr_t signum, const go_sigaction_t *goact, go_sigaction_t *oldgoact) { + int32_t ret; + struct sigaction act; + struct sigaction oldact; + size_t i; + + _cgo_tsan_acquire(); + + memset(&act, 0, sizeof act); + memset(&oldact, 0, sizeof oldact); + + if (goact) { + if (goact->flags & SA_SIGINFO) { + act.sa_sigaction = (void(*)(int, siginfo_t*, void*))(goact->handler); + } else { + act.sa_handler = (void(*)(int))(goact->handler); + } + sigemptyset(&act.sa_mask); + for (i = 0; i < 8 * sizeof(goact->mask); i++) { + if (goact->mask & ((uint64_t)(1)<<i)) { + sigaddset(&act.sa_mask, (int)(i+1)); + } + } + act.sa_flags = (int)(goact->flags & ~(uint64_t)SA_RESTORER); + } + + ret = sigaction((int)signum, goact ? &act : NULL, oldgoact ? &oldact : NULL); + if (ret == -1) { + // runtime.rt_sigaction expects _cgo_sigaction to return errno on error. + _cgo_tsan_release(); + return errno; + } + + if (oldgoact) { + if (oldact.sa_flags & SA_SIGINFO) { + oldgoact->handler = (uintptr_t)(oldact.sa_sigaction); + } else { + oldgoact->handler = (uintptr_t)(oldact.sa_handler); + } + oldgoact->mask = 0; + for (i = 0; i < 8 * sizeof(oldgoact->mask); i++) { + if (sigismember(&oldact.sa_mask, (int)(i+1)) == 1) { + oldgoact->mask |= (uint64_t)(1)<<i; + } + } + oldgoact->flags = (uint64_t)oldact.sa_flags; + } + + _cgo_tsan_release(); + return ret; +} diff --git a/src/runtime/cgo/gcc_signal2_ios_arm64.c b/src/runtime/cgo/gcc_signal2_ios_arm64.c new file mode 100644 index 0000000..f8cef54 --- /dev/null +++ b/src/runtime/cgo/gcc_signal2_ios_arm64.c @@ -0,0 +1,11 @@ +// 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 lldb + +// Used by gcc_signal_darwin_arm64.c when doing the test build during cgo. +// We hope that for real binaries the definition provided by Go will take precedence +// and the linker will drop this .o file altogether, which is why this definition +// is all by itself in its own file. +void __attribute__((weak)) xx_cgo_panicmem(void) {} diff --git a/src/runtime/cgo/gcc_signal_ios_arm64.c b/src/runtime/cgo/gcc_signal_ios_arm64.c new file mode 100644 index 0000000..87055e9 --- /dev/null +++ b/src/runtime/cgo/gcc_signal_ios_arm64.c @@ -0,0 +1,213 @@ +// 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. + +// Emulation of the Unix signal SIGSEGV. +// +// On iOS, Go tests and apps under development are run by lldb. +// The debugger uses a task-level exception handler to intercept signals. +// Despite having a 'handle' mechanism like gdb, lldb will not allow a +// SIGSEGV to pass to the running program. For Go, this means we cannot +// generate a panic, which cannot be recovered, and so tests fail. +// +// We work around this by registering a thread-level mach exception handler +// and intercepting EXC_BAD_ACCESS. The kernel offers thread handlers a +// chance to resolve exceptions before the task handler, so we can generate +// the panic and avoid lldb's SIGSEGV handler. +// +// The dist tool enables this by build flag when testing. + +//go:build lldb + +#include <limits.h> +#include <pthread.h> +#include <stdio.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> + +#include <mach/arm/thread_status.h> +#include <mach/exception_types.h> +#include <mach/mach.h> +#include <mach/mach_init.h> +#include <mach/mach_port.h> +#include <mach/thread_act.h> +#include <mach/thread_status.h> + +#include "libcgo.h" +#include "libcgo_unix.h" + +void xx_cgo_panicmem(void); +uintptr_t x_cgo_panicmem = (uintptr_t)xx_cgo_panicmem; + +static pthread_mutex_t mach_exception_handler_port_set_mu; +static mach_port_t mach_exception_handler_port_set = MACH_PORT_NULL; + +kern_return_t +catch_exception_raise( + mach_port_t exception_port, + mach_port_t thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code_vector, + mach_msg_type_number_t code_count) +{ + kern_return_t ret; + arm_unified_thread_state_t thread_state; + mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT; + + // Returning KERN_SUCCESS intercepts the exception. + // + // Returning KERN_FAILURE lets the exception fall through to the + // next handler, which is the standard signal emulation code + // registered on the task port. + + if (exception != EXC_BAD_ACCESS) { + return KERN_FAILURE; + } + + ret = thread_get_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, &state_count); + if (ret) { + fprintf(stderr, "runtime/cgo: thread_get_state failed: %d\n", ret); + abort(); + } + + // Bounce call to sigpanic through asm that makes it look like + // we call sigpanic directly from the faulting code. +#ifdef __arm64__ + thread_state.ts_64.__x[1] = thread_state.ts_64.__lr; + thread_state.ts_64.__x[2] = thread_state.ts_64.__pc; + thread_state.ts_64.__pc = x_cgo_panicmem; +#else + thread_state.ts_32.__r[1] = thread_state.ts_32.__lr; + thread_state.ts_32.__r[2] = thread_state.ts_32.__pc; + thread_state.ts_32.__pc = x_cgo_panicmem; +#endif + + if (0) { + // Useful debugging logic when panicmem is broken. + // + // Sends the first SIGSEGV and lets lldb catch the + // second one, avoiding a loop that locks up iOS + // devices requiring a hard reboot. + fprintf(stderr, "runtime/cgo: caught exc_bad_access\n"); + fprintf(stderr, "__lr = %llx\n", thread_state.ts_64.__lr); + fprintf(stderr, "__pc = %llx\n", thread_state.ts_64.__pc); + static int pass1 = 0; + if (pass1) { + return KERN_FAILURE; + } + pass1 = 1; + } + + ret = thread_set_state(thread, ARM_UNIFIED_THREAD_STATE, (thread_state_t)&thread_state, state_count); + if (ret) { + fprintf(stderr, "runtime/cgo: thread_set_state failed: %d\n", ret); + abort(); + } + + return KERN_SUCCESS; +} + +void +darwin_arm_init_thread_exception_port() +{ + // Called by each new OS thread to bind its EXC_BAD_ACCESS exception + // to mach_exception_handler_port_set. + int ret; + mach_port_t port = MACH_PORT_NULL; + + ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port); + if (ret) { + fprintf(stderr, "runtime/cgo: mach_port_allocate failed: %d\n", ret); + abort(); + } + ret = mach_port_insert_right( + mach_task_self(), + port, + port, + MACH_MSG_TYPE_MAKE_SEND); + if (ret) { + fprintf(stderr, "runtime/cgo: mach_port_insert_right failed: %d\n", ret); + abort(); + } + + ret = thread_set_exception_ports( + mach_thread_self(), + EXC_MASK_BAD_ACCESS, + port, + EXCEPTION_DEFAULT, + THREAD_STATE_NONE); + if (ret) { + fprintf(stderr, "runtime/cgo: thread_set_exception_ports failed: %d\n", ret); + abort(); + } + + ret = pthread_mutex_lock(&mach_exception_handler_port_set_mu); + if (ret) { + fprintf(stderr, "runtime/cgo: pthread_mutex_lock failed: %d\n", ret); + abort(); + } + ret = mach_port_move_member( + mach_task_self(), + port, + mach_exception_handler_port_set); + if (ret) { + fprintf(stderr, "runtime/cgo: mach_port_move_member failed: %d\n", ret); + abort(); + } + ret = pthread_mutex_unlock(&mach_exception_handler_port_set_mu); + if (ret) { + fprintf(stderr, "runtime/cgo: pthread_mutex_unlock failed: %d\n", ret); + abort(); + } +} + +static void* +mach_exception_handler(void *port) +{ + // Calls catch_exception_raise. + extern boolean_t exc_server(); + mach_msg_server(exc_server, 2048, (mach_port_t)port, 0); + abort(); // never returns +} + +void +darwin_arm_init_mach_exception_handler() +{ + pthread_mutex_init(&mach_exception_handler_port_set_mu, NULL); + + // Called once per process to initialize a mach port server, listening + // for EXC_BAD_ACCESS thread exceptions. + int ret; + pthread_t thr = NULL; + pthread_attr_t attr; + sigset_t ign, oset; + + ret = mach_port_allocate( + mach_task_self(), + MACH_PORT_RIGHT_PORT_SET, + &mach_exception_handler_port_set); + if (ret) { + fprintf(stderr, "runtime/cgo: mach_port_allocate failed for port_set: %d\n", ret); + abort(); + } + + // Block all signals to the exception handler thread + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + // Start a thread to handle exceptions. + uintptr_t port_set = (uintptr_t)mach_exception_handler_port_set; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + ret = _cgo_try_pthread_create(&thr, &attr, mach_exception_handler, (void*)port_set); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (ret) { + fprintf(stderr, "runtime/cgo: pthread_create failed: %d\n", ret); + abort(); + } + pthread_attr_destroy(&attr); +} diff --git a/src/runtime/cgo/gcc_signal_ios_nolldb.c b/src/runtime/cgo/gcc_signal_ios_nolldb.c new file mode 100644 index 0000000..9ddc37a --- /dev/null +++ b/src/runtime/cgo/gcc_signal_ios_nolldb.c @@ -0,0 +1,10 @@ +// 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 !lldb && ios && arm64 + +#include <stdint.h> + +void darwin_arm_init_thread_exception_port() {} +void darwin_arm_init_mach_exception_handler() {} diff --git a/src/runtime/cgo/gcc_solaris_amd64.c b/src/runtime/cgo/gcc_solaris_amd64.c new file mode 100644 index 0000000..9b106a6 --- /dev/null +++ b/src/runtime/cgo/gcc_solaris_amd64.c @@ -0,0 +1,83 @@ +// 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 <pthread.h> +#include <string.h> +#include <signal.h> +#include <ucontext.h> +#include "libcgo.h" +#include "libcgo_unix.h" + +static void* threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + ucontext_t ctx; + + setg_gcc = setg; + if (getcontext(&ctx) != 0) + perror("runtime/cgo: getcontext failed"); + g->stacklo = (uintptr_t)ctx.uc_stack.ss_sp; + + // Solaris processes report a tiny stack when run with "ulimit -s unlimited". + // Correct that as best we can: assume it's at least 1 MB. + // See golang.org/issue/12210. + if(ctx.uc_stack.ss_size < 1024*1024) + g->stacklo -= 1024*1024 - ctx.uc_stack.ss_size; + + // Sanity check the results now, rather than getting a + // morestack on g0 crash. + if (g->stacklo >= g->stackhi) { + fatalf("bad stack bounds: lo=%p hi=%p", (void*)(g->stacklo), (void*)(g->stackhi)); + } +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + pthread_attr_t attr; + sigset_t ign, oset; + pthread_t p; + void *base; + size_t size; + int err; + + sigfillset(&ign); + pthread_sigmask(SIG_SETMASK, &ign, &oset); + + pthread_attr_init(&attr); + + if (pthread_attr_getstack(&attr, &base, &size) != 0) + perror("runtime/cgo: pthread_attr_getstack failed"); + if (size == 0) { + ts->g->stackhi = 2 << 20; + if (pthread_attr_setstack(&attr, NULL, ts->g->stackhi) != 0) + perror("runtime/cgo: pthread_attr_setstack failed"); + } else { + ts->g->stackhi = size; + } + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + err = _cgo_try_pthread_create(&p, &attr, threadentry, ts); + + pthread_sigmask(SIG_SETMASK, &oset, nil); + + if (err != 0) { + fatalf("pthread_create failed: %s", strerror(err)); + } +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void* +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); + return nil; +} diff --git a/src/runtime/cgo/gcc_stack_darwin.c b/src/runtime/cgo/gcc_stack_darwin.c new file mode 100644 index 0000000..0a9038e --- /dev/null +++ b/src/runtime/cgo/gcc_stack_darwin.c @@ -0,0 +1,20 @@ +// 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 <pthread.h> +#include "libcgo.h" + +void +x_cgo_getstackbound(uintptr bounds[2]) +{ + void* addr; + size_t size; + pthread_t p; + + p = pthread_self(); + addr = pthread_get_stackaddr_np(p); // high address (!) + size = pthread_get_stacksize_np(p); + bounds[0] = (uintptr)addr - size; + bounds[1] = (uintptr)addr; +} diff --git a/src/runtime/cgo/gcc_stack_unix.c b/src/runtime/cgo/gcc_stack_unix.c new file mode 100644 index 0000000..f3fead9 --- /dev/null +++ b/src/runtime/cgo/gcc_stack_unix.c @@ -0,0 +1,40 @@ +// 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 && !darwin + +#ifndef _GNU_SOURCE // pthread_getattr_np +#define _GNU_SOURCE +#endif + +#include <pthread.h> +#include "libcgo.h" + +void +x_cgo_getstackbound(uintptr bounds[2]) +{ + pthread_attr_t attr; + void *addr; + size_t size; + +#if defined(__GLIBC__) || (defined(__sun) && !defined(__illumos__)) + // pthread_getattr_np is a GNU extension supported in glibc. + // Solaris is not glibc but does support pthread_getattr_np + // (and the fallback doesn't work...). Illumos does not. + pthread_getattr_np(pthread_self(), &attr); // GNU extension + pthread_attr_getstack(&attr, &addr, &size); // low address +#elif defined(__illumos__) + pthread_attr_init(&attr); + pthread_attr_get_np(pthread_self(), &attr); + pthread_attr_getstack(&attr, &addr, &size); // low address +#else + pthread_attr_init(&attr); + pthread_attr_getstacksize(&attr, &size); + addr = __builtin_frame_address(0) + 4096 - size; +#endif + pthread_attr_destroy(&attr); + + bounds[0] = (uintptr)addr; + bounds[1] = (uintptr)addr + size; +} diff --git a/src/runtime/cgo/gcc_stack_windows.c b/src/runtime/cgo/gcc_stack_windows.c new file mode 100644 index 0000000..d798cc7 --- /dev/null +++ b/src/runtime/cgo/gcc_stack_windows.c @@ -0,0 +1,7 @@ +// 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 "libcgo.h" + +void x_cgo_getstackbound(uintptr bounds[2]) {} // no-op for now diff --git a/src/runtime/cgo/gcc_traceback.c b/src/runtime/cgo/gcc_traceback.c new file mode 100644 index 0000000..c6643a1 --- /dev/null +++ b/src/runtime/cgo/gcc_traceback.c @@ -0,0 +1,46 @@ +// 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 darwin || linux + +#include <stdint.h> +#include "libcgo.h" + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#if __has_feature(memory_sanitizer) +#include <sanitizer/msan_interface.h> +#endif + +// Call the user's traceback function and then call sigtramp. +// The runtime signal handler will jump to this code. +// We do it this way so that the user's traceback function will be called +// by a C function with proper unwind info. +void +x_cgo_callers(uintptr_t sig, void *info, void *context, void (*cgoTraceback)(struct cgoTracebackArg*), uintptr_t* cgoCallers, void (*sigtramp)(uintptr_t, void*, void*)) { + struct cgoTracebackArg arg; + + arg.Context = 0; + arg.SigContext = (uintptr_t)(context); + arg.Buf = cgoCallers; + arg.Max = 32; // must match len(runtime.cgoCallers) + +#if __has_feature(memory_sanitizer) + // This function is called directly from the signal handler. + // The arguments are passed in registers, so whether msan + // considers cgoCallers to be initialized depends on whether + // it considers the appropriate register to be initialized. + // That can cause false reports in rare cases. + // Explicitly unpoison the memory to avoid that. + // See issue #47543 for more details. + __msan_unpoison(&arg, sizeof arg); +#endif + + _cgo_tsan_acquire(); + (*cgoTraceback)(&arg); + _cgo_tsan_release(); + sigtramp(sig, info, context); +} diff --git a/src/runtime/cgo/gcc_util.c b/src/runtime/cgo/gcc_util.c new file mode 100644 index 0000000..3fcb48c --- /dev/null +++ b/src/runtime/cgo/gcc_util.c @@ -0,0 +1,69 @@ +// 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 "libcgo.h" + +/* Stub for creating a new thread */ +void +x_cgo_thread_start(ThreadStart *arg) +{ + ThreadStart *ts; + + /* Make our own copy that can persist after we return. */ + _cgo_tsan_acquire(); + ts = malloc(sizeof *ts); + _cgo_tsan_release(); + if(ts == nil) { + fprintf(stderr, "runtime/cgo: out of memory in thread_start\n"); + abort(); + } + *ts = *arg; + + _cgo_sys_thread_start(ts); /* OS-dependent half */ +} + +#ifndef CGO_TSAN +void(* const _cgo_yield)() = NULL; +#else + +#include <string.h> + +char x_cgo_yield_strncpy_src = 0; +char x_cgo_yield_strncpy_dst = 0; +size_t x_cgo_yield_strncpy_n = 0; + +/* +Stub for allowing libc interceptors to execute. + +_cgo_yield is set to NULL if we do not expect libc interceptors to exist. +*/ +static void +x_cgo_yield() +{ + /* + The libc function(s) we call here must form a no-op and include at least one + call that triggers TSAN to process pending asynchronous signals. + + sleep(0) would be fine, but it's not portable C (so it would need more header + guards). + free(NULL) has a fast-path special case in TSAN, so it doesn't + trigger signal delivery. + free(malloc(0)) would work (triggering the interceptors in malloc), but + it also runs a bunch of user-supplied malloc hooks. + + So we choose strncpy(_, _, 0): it requires an extra header, + but it's standard and should be very efficient. + + GCC 7 has an unfortunate habit of optimizing out strncpy calls (see + https://golang.org/issue/21196), so the arguments here need to be global + variables with external linkage in order to ensure that the call traps all the + way down into libc. + */ + strncpy(&x_cgo_yield_strncpy_dst, &x_cgo_yield_strncpy_src, + x_cgo_yield_strncpy_n); +} + +void(* const _cgo_yield)() = &x_cgo_yield; + +#endif /* GO_TSAN */ diff --git a/src/runtime/cgo/gcc_windows_386.c b/src/runtime/cgo/gcc_windows_386.c new file mode 100644 index 0000000..983e14b --- /dev/null +++ b/src/runtime/cgo/gcc_windows_386.c @@ -0,0 +1,53 @@ +// 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. + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include "libcgo.h" +#include "libcgo_windows.h" + +static void threadentry(void*); +static void (*setg_gcc)(void*); +static DWORD *tls_g; + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + setg_gcc = setg; + tls_g = (DWORD *)tlsg; +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + _cgo_beginthread(threadentry, ts); +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + // minit queries stack bounds from the OS. + + /* + * Set specific keys in thread local storage. + */ + asm volatile ( + "movl %0, %%fs:0(%1)\n" // MOVL tls0, 0(tls_g)(FS) + "movl %%fs:0(%1), %%eax\n" // MOVL 0(tls_g)(FS), tmp + "movl %2, 0(%%eax)\n" // MOVL g, 0(AX) + :: "r"(ts.tls), "r"(*tls_g), "r"(ts.g) : "%eax" + ); + + crosscall1(ts.fn, setg_gcc, ts.g); +} diff --git a/src/runtime/cgo/gcc_windows_amd64.c b/src/runtime/cgo/gcc_windows_amd64.c new file mode 100644 index 0000000..e26887a --- /dev/null +++ b/src/runtime/cgo/gcc_windows_amd64.c @@ -0,0 +1,52 @@ +// 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. + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include "libcgo.h" +#include "libcgo_windows.h" + +static void threadentry(void*); +static void (*setg_gcc)(void*); +static DWORD *tls_g; + +void +x_cgo_init(G *g, void (*setg)(void*), void **tlsg, void **tlsbase) +{ + setg_gcc = setg; + tls_g = (DWORD *)tlsg; +} + + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + _cgo_beginthread(threadentry, ts); +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); +static void +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + // minit queries stack bounds from the OS. + + /* + * Set specific keys in thread local storage. + */ + asm volatile ( + "movq %0, %%gs:0(%1)\n" // MOVL tls0, 0(tls_g)(GS) + :: "r"(ts.tls), "r"(*tls_g) + ); + + crosscall1(ts.fn, setg_gcc, (void*)ts.g); +} diff --git a/src/runtime/cgo/gcc_windows_arm64.c b/src/runtime/cgo/gcc_windows_arm64.c new file mode 100644 index 0000000..8f113cc --- /dev/null +++ b/src/runtime/cgo/gcc_windows_arm64.c @@ -0,0 +1,40 @@ +// 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. + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <process.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include "libcgo.h" +#include "libcgo_windows.h" + +static void threadentry(void*); +static void (*setg_gcc)(void*); + +void +x_cgo_init(G *g, void (*setg)(void*)) +{ + setg_gcc = setg; +} + +void +_cgo_sys_thread_start(ThreadStart *ts) +{ + _cgo_beginthread(threadentry, ts); +} + +extern void crosscall1(void (*fn)(void), void (*setg_gcc)(void*), void *g); + +static void +threadentry(void *v) +{ + ThreadStart ts; + + ts = *(ThreadStart*)v; + free(v); + + crosscall1(ts.fn, setg_gcc, (void *)ts.g); +} diff --git a/src/runtime/cgo/handle.go b/src/runtime/cgo/handle.go new file mode 100644 index 0000000..59b65da --- /dev/null +++ b/src/runtime/cgo/handle.go @@ -0,0 +1,144 @@ +// 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 cgo + +import ( + "sync" + "sync/atomic" +) + +// Handle provides a way to pass values that contain Go pointers +// (pointers to memory allocated by Go) between Go and C without +// breaking the cgo pointer passing rules. A Handle is an integer +// value that can represent any Go value. A Handle can be passed +// through C and back to Go, and Go code can use the Handle to +// retrieve the original Go value. +// +// The underlying type of Handle is guaranteed to fit in an integer type +// that is large enough to hold the bit pattern of any pointer. The zero +// value of a Handle is not valid, and thus is safe to use as a sentinel +// in C APIs. +// +// For instance, on the Go side: +// +// package main +// +// /* +// #include <stdint.h> // for uintptr_t +// +// extern void MyGoPrint(uintptr_t handle); +// void myprint(uintptr_t handle); +// */ +// import "C" +// import "runtime/cgo" +// +// //export MyGoPrint +// func MyGoPrint(handle C.uintptr_t) { +// h := cgo.Handle(handle) +// val := h.Value().(string) +// println(val) +// h.Delete() +// } +// +// func main() { +// val := "hello Go" +// C.myprint(C.uintptr_t(cgo.NewHandle(val))) +// // Output: hello Go +// } +// +// and on the C side: +// +// #include <stdint.h> // for uintptr_t +// +// // A Go function +// extern void MyGoPrint(uintptr_t handle); +// +// // A C function +// void myprint(uintptr_t handle) { +// MyGoPrint(handle); +// } +// +// Some C functions accept a void* argument that points to an arbitrary +// data value supplied by the caller. It is not safe to coerce a [cgo.Handle] +// (an integer) to a Go [unsafe.Pointer], but instead we can pass the address +// of the cgo.Handle to the void* parameter, as in this variant of the +// previous example: +// +// package main +// +// /* +// extern void MyGoPrint(void *context); +// static inline void myprint(void *context) { +// MyGoPrint(context); +// } +// */ +// import "C" +// import ( +// "runtime/cgo" +// "unsafe" +// ) +// +// //export MyGoPrint +// func MyGoPrint(context unsafe.Pointer) { +// h := *(*cgo.Handle)(context) +// val := h.Value().(string) +// println(val) +// h.Delete() +// } +// +// func main() { +// val := "hello Go" +// h := cgo.NewHandle(val) +// C.myprint(unsafe.Pointer(&h)) +// // Output: hello Go +// } +type Handle uintptr + +// NewHandle returns a handle for a given value. +// +// The handle is valid until the program calls Delete on it. The handle +// uses resources, and this package assumes that C code may hold on to +// the handle, so a program must explicitly call Delete when the handle +// is no longer needed. +// +// The intended use is to pass the returned handle to C code, which +// passes it back to Go, which calls Value. +func NewHandle(v any) Handle { + h := handleIdx.Add(1) + if h == 0 { + panic("runtime/cgo: ran out of handle space") + } + + handles.Store(h, v) + return Handle(h) +} + +// Value returns the associated Go value for a valid handle. +// +// The method panics if the handle is invalid. +func (h Handle) Value() any { + v, ok := handles.Load(uintptr(h)) + if !ok { + panic("runtime/cgo: misuse of an invalid Handle") + } + return v +} + +// Delete invalidates a handle. This method should only be called once +// the program no longer needs to pass the handle to C and the C code +// no longer has a copy of the handle value. +// +// The method panics if the handle is invalid. +func (h Handle) Delete() { + _, ok := handles.LoadAndDelete(uintptr(h)) + if !ok { + panic("runtime/cgo: misuse of an invalid Handle") + } +} + +var ( + handles = sync.Map{} // map[Handle]interface{} + handleIdx atomic.Uintptr +) diff --git a/src/runtime/cgo/handle_test.go b/src/runtime/cgo/handle_test.go new file mode 100644 index 0000000..b341c8e --- /dev/null +++ b/src/runtime/cgo/handle_test.go @@ -0,0 +1,103 @@ +// 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 cgo + +import ( + "reflect" + "testing" +) + +func TestHandle(t *testing.T) { + v := 42 + + tests := []struct { + v1 any + v2 any + }{ + {v1: v, v2: v}, + {v1: &v, v2: &v}, + {v1: nil, v2: nil}, + } + + for _, tt := range tests { + h1 := NewHandle(tt.v1) + h2 := NewHandle(tt.v2) + + if uintptr(h1) == 0 || uintptr(h2) == 0 { + t.Fatalf("NewHandle returns zero") + } + + if uintptr(h1) == uintptr(h2) { + t.Fatalf("Duplicated Go values should have different handles, but got equal") + } + + h1v := h1.Value() + h2v := h2.Value() + if !reflect.DeepEqual(h1v, h2v) || !reflect.DeepEqual(h1v, tt.v1) { + t.Fatalf("Value of a Handle got wrong, got %+v %+v, want %+v", h1v, h2v, tt.v1) + } + + h1.Delete() + h2.Delete() + } + + siz := 0 + handles.Range(func(k, v any) bool { + siz++ + return true + }) + if siz != 0 { + t.Fatalf("handles are not cleared, got %d, want %d", siz, 0) + } +} + +func TestInvalidHandle(t *testing.T) { + t.Run("zero", func(t *testing.T) { + h := Handle(0) + + defer func() { + if r := recover(); r != nil { + return + } + t.Fatalf("Delete of zero handle did not trigger a panic") + }() + + h.Delete() + }) + + t.Run("invalid", func(t *testing.T) { + h := NewHandle(42) + + defer func() { + if r := recover(); r != nil { + h.Delete() + return + } + t.Fatalf("Invalid handle did not trigger a panic") + }() + + Handle(h + 1).Delete() + }) +} + +func BenchmarkHandle(b *testing.B) { + b.Run("non-concurrent", func(b *testing.B) { + for i := 0; i < b.N; i++ { + h := NewHandle(i) + _ = h.Value() + h.Delete() + } + }) + b.Run("concurrent", func(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + var v int + for pb.Next() { + h := NewHandle(v) + _ = h.Value() + h.Delete() + } + }) + }) +} diff --git a/src/runtime/cgo/iscgo.go b/src/runtime/cgo/iscgo.go new file mode 100644 index 0000000..e12d0f4 --- /dev/null +++ b/src/runtime/cgo/iscgo.go @@ -0,0 +1,17 @@ +// 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. + +// The runtime package contains an uninitialized definition +// for runtime·iscgo. Override it to tell the runtime we're here. +// There are various function pointers that should be set too, +// but those depend on dynamic linker magic to get initialized +// correctly, and sometimes they break. This variable is a +// backup: it depends only on old C style static linking rules. + +package cgo + +import _ "unsafe" // for go:linkname + +//go:linkname _iscgo runtime.iscgo +var _iscgo bool = true diff --git a/src/runtime/cgo/libcgo.h b/src/runtime/cgo/libcgo.h new file mode 100644 index 0000000..295c12c --- /dev/null +++ b/src/runtime/cgo/libcgo.h @@ -0,0 +1,151 @@ +// 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 <stdint.h> +#include <stdlib.h> +#include <stdio.h> + +#undef nil +#define nil ((void*)0) +#define nelem(x) (sizeof(x)/sizeof((x)[0])) + +typedef uint32_t uint32; +typedef uint64_t uint64; +typedef uintptr_t uintptr; + +/* + * The beginning of the per-goroutine structure, + * as defined in ../pkg/runtime/runtime.h. + * Just enough to edit these two fields. + */ +typedef struct G G; +struct G +{ + uintptr stacklo; + uintptr stackhi; +}; + +/* + * Arguments to the _cgo_thread_start call. + * Also known to ../pkg/runtime/runtime.h. + */ +typedef struct ThreadStart ThreadStart; +struct ThreadStart +{ + G *g; + uintptr *tls; + void (*fn)(void); +}; + +/* + * Called by 5c/6c/8c world. + * Makes a local copy of the ThreadStart and + * calls _cgo_sys_thread_start(ts). + */ +extern void (*_cgo_thread_start)(ThreadStart *ts); + +/* + * Creates a new operating system thread without updating any Go state + * (OS dependent). + */ +extern void (*_cgo_sys_thread_create)(void* (*func)(void*), void* arg); + +/* + * Indicates whether a dummy pthread per-thread variable is allocated. + */ +extern uintptr_t *_cgo_pthread_key_created; + +/* + * Creates the new operating system thread (OS, arch dependent). + */ +void _cgo_sys_thread_start(ThreadStart *ts); + +/* + * Waits for the Go runtime to be initialized (OS dependent). + * If runtime.SetCgoTraceback is used to set a context function, + * calls the context function and returns the context value. + */ +uintptr_t _cgo_wait_runtime_init_done(void); + +/* + * Get the low and high boundaries of the stack. + */ +void x_cgo_getstackbound(uintptr bounds[2]); + +/* + * Prints error then calls abort. For linux and android. + */ +void fatalf(const char* format, ...); + +/* + * Registers the current mach thread port for EXC_BAD_ACCESS processing. + */ +void darwin_arm_init_thread_exception_port(void); + +/* + * Starts a mach message server processing EXC_BAD_ACCESS. + */ +void darwin_arm_init_mach_exception_handler(void); + +/* + * The cgo context function. See runtime.SetCgoTraceback. + */ +struct context_arg { + uintptr_t Context; +}; +extern void (*(_cgo_get_context_function(void)))(struct context_arg*); + +/* + * The argument for the cgo traceback callback. See runtime.SetCgoTraceback. + */ +struct cgoTracebackArg { + uintptr_t Context; + uintptr_t SigContext; + uintptr_t* Buf; + uintptr_t Max; +}; + +/* + * TSAN support. This is only useful when building with + * CGO_CFLAGS="-fsanitize=thread" CGO_LDFLAGS="-fsanitize=thread" go install + */ +#undef CGO_TSAN +#if defined(__has_feature) +# if __has_feature(thread_sanitizer) +# define CGO_TSAN +# endif +#elif defined(__SANITIZE_THREAD__) +# define CGO_TSAN +#endif + +#ifdef CGO_TSAN + +// These must match the definitions in yesTsanProlog in cmd/cgo/out.go. +// In general we should call _cgo_tsan_acquire when we enter C code, +// and call _cgo_tsan_release when we return to Go code. +// This is only necessary when calling code that might be instrumented +// by TSAN, which mostly means system library calls that TSAN intercepts. +// See the comment in cmd/cgo/out.go for more details. + +long long _cgo_sync __attribute__ ((common)); + +extern void __tsan_acquire(void*); +extern void __tsan_release(void*); + +__attribute__ ((unused)) +static void _cgo_tsan_acquire() { + __tsan_acquire(&_cgo_sync); +} + +__attribute__ ((unused)) +static void _cgo_tsan_release() { + __tsan_release(&_cgo_sync); +} + +#else // !defined(CGO_TSAN) + +#define _cgo_tsan_acquire() +#define _cgo_tsan_release() + +#endif // !defined(CGO_TSAN) diff --git a/src/runtime/cgo/libcgo_unix.h b/src/runtime/cgo/libcgo_unix.h new file mode 100644 index 0000000..b8f8d30 --- /dev/null +++ b/src/runtime/cgo/libcgo_unix.h @@ -0,0 +1,20 @@ +// 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. + +/* + * Initialize g->stacklo. + */ +extern void _cgo_set_stacklo(G *, uintptr *); + +/* + * Call pthread_create, retrying on EAGAIN. + */ +extern int _cgo_try_pthread_create(pthread_t*, const pthread_attr_t*, void* (*)(void*), void*); + +/* + * Same as _cgo_try_pthread_create, but passing on the pthread_create function. + * Only defined on OpenBSD. + */ +extern int _cgo_openbsd_try_pthread_create(int (*)(pthread_t*, const pthread_attr_t*, void *(*pfn)(void*), void*), + pthread_t*, const pthread_attr_t*, void* (*)(void*), void* arg); diff --git a/src/runtime/cgo/libcgo_windows.h b/src/runtime/cgo/libcgo_windows.h new file mode 100644 index 0000000..33d7637 --- /dev/null +++ b/src/runtime/cgo/libcgo_windows.h @@ -0,0 +1,6 @@ +// 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. + +// Call _beginthread, aborting on failure. +void _cgo_beginthread(void (*func)(void*), void* arg); diff --git a/src/runtime/cgo/linux.go b/src/runtime/cgo/linux.go new file mode 100644 index 0000000..1d6fe03 --- /dev/null +++ b/src/runtime/cgo/linux.go @@ -0,0 +1,74 @@ +// 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. + +// Linux system call wrappers that provide POSIX semantics through the +// corresponding cgo->libc (nptl) wrappers for various system calls. + +//go:build linux + +package cgo + +import "unsafe" + +// Each of the following entries is needed to ensure that the +// syscall.syscall_linux code can conditionally call these +// function pointers: +// +// 1. find the C-defined function start +// 2. force the local byte alias to be mapped to that location +// 3. map the Go pointer to the function to the syscall package + +//go:cgo_import_static _cgo_libc_setegid +//go:linkname _cgo_libc_setegid _cgo_libc_setegid +//go:linkname cgo_libc_setegid syscall.cgo_libc_setegid +var _cgo_libc_setegid byte +var cgo_libc_setegid = unsafe.Pointer(&_cgo_libc_setegid) + +//go:cgo_import_static _cgo_libc_seteuid +//go:linkname _cgo_libc_seteuid _cgo_libc_seteuid +//go:linkname cgo_libc_seteuid syscall.cgo_libc_seteuid +var _cgo_libc_seteuid byte +var cgo_libc_seteuid = unsafe.Pointer(&_cgo_libc_seteuid) + +//go:cgo_import_static _cgo_libc_setregid +//go:linkname _cgo_libc_setregid _cgo_libc_setregid +//go:linkname cgo_libc_setregid syscall.cgo_libc_setregid +var _cgo_libc_setregid byte +var cgo_libc_setregid = unsafe.Pointer(&_cgo_libc_setregid) + +//go:cgo_import_static _cgo_libc_setresgid +//go:linkname _cgo_libc_setresgid _cgo_libc_setresgid +//go:linkname cgo_libc_setresgid syscall.cgo_libc_setresgid +var _cgo_libc_setresgid byte +var cgo_libc_setresgid = unsafe.Pointer(&_cgo_libc_setresgid) + +//go:cgo_import_static _cgo_libc_setresuid +//go:linkname _cgo_libc_setresuid _cgo_libc_setresuid +//go:linkname cgo_libc_setresuid syscall.cgo_libc_setresuid +var _cgo_libc_setresuid byte +var cgo_libc_setresuid = unsafe.Pointer(&_cgo_libc_setresuid) + +//go:cgo_import_static _cgo_libc_setreuid +//go:linkname _cgo_libc_setreuid _cgo_libc_setreuid +//go:linkname cgo_libc_setreuid syscall.cgo_libc_setreuid +var _cgo_libc_setreuid byte +var cgo_libc_setreuid = unsafe.Pointer(&_cgo_libc_setreuid) + +//go:cgo_import_static _cgo_libc_setgroups +//go:linkname _cgo_libc_setgroups _cgo_libc_setgroups +//go:linkname cgo_libc_setgroups syscall.cgo_libc_setgroups +var _cgo_libc_setgroups byte +var cgo_libc_setgroups = unsafe.Pointer(&_cgo_libc_setgroups) + +//go:cgo_import_static _cgo_libc_setgid +//go:linkname _cgo_libc_setgid _cgo_libc_setgid +//go:linkname cgo_libc_setgid syscall.cgo_libc_setgid +var _cgo_libc_setgid byte +var cgo_libc_setgid = unsafe.Pointer(&_cgo_libc_setgid) + +//go:cgo_import_static _cgo_libc_setuid +//go:linkname _cgo_libc_setuid _cgo_libc_setuid +//go:linkname cgo_libc_setuid syscall.cgo_libc_setuid +var _cgo_libc_setuid byte +var cgo_libc_setuid = unsafe.Pointer(&_cgo_libc_setuid) diff --git a/src/runtime/cgo/linux_syscall.c b/src/runtime/cgo/linux_syscall.c new file mode 100644 index 0000000..0ea2da7 --- /dev/null +++ b/src/runtime/cgo/linux_syscall.c @@ -0,0 +1,85 @@ +// 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 linux + +#ifndef _GNU_SOURCE // setres[ug]id() API. +#define _GNU_SOURCE +#endif + +#include <grp.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include "libcgo.h" + +/* + * Assumed POSIX compliant libc system call wrappers. For linux, the + * glibc/nptl/setxid mechanism ensures that POSIX semantics are + * honored for all pthreads (by default), and this in turn with cgo + * ensures that all Go threads launched with cgo are kept in sync for + * these function calls. + */ + +// argset_t matches runtime/cgocall.go:argset. +typedef struct { + uintptr_t* args; + uintptr_t retval; +} argset_t; + +// libc backed posix-compliant syscalls. + +#define SET_RETVAL(fn) \ + uintptr_t ret = (uintptr_t) fn ; \ + if (ret == (uintptr_t) -1) { \ + x->retval = (uintptr_t) errno; \ + } else \ + x->retval = ret + +void +_cgo_libc_setegid(argset_t* x) { + SET_RETVAL(setegid((gid_t) x->args[0])); +} + +void +_cgo_libc_seteuid(argset_t* x) { + SET_RETVAL(seteuid((uid_t) x->args[0])); +} + +void +_cgo_libc_setgid(argset_t* x) { + SET_RETVAL(setgid((gid_t) x->args[0])); +} + +void +_cgo_libc_setgroups(argset_t* x) { + SET_RETVAL(setgroups((size_t) x->args[0], (const gid_t *) x->args[1])); +} + +void +_cgo_libc_setregid(argset_t* x) { + SET_RETVAL(setregid((gid_t) x->args[0], (gid_t) x->args[1])); +} + +void +_cgo_libc_setresgid(argset_t* x) { + SET_RETVAL(setresgid((gid_t) x->args[0], (gid_t) x->args[1], + (gid_t) x->args[2])); +} + +void +_cgo_libc_setresuid(argset_t* x) { + SET_RETVAL(setresuid((uid_t) x->args[0], (uid_t) x->args[1], + (uid_t) x->args[2])); +} + +void +_cgo_libc_setreuid(argset_t* x) { + SET_RETVAL(setreuid((uid_t) x->args[0], (uid_t) x->args[1])); +} + +void +_cgo_libc_setuid(argset_t* x) { + SET_RETVAL(setuid((uid_t) x->args[0])); +} diff --git a/src/runtime/cgo/mmap.go b/src/runtime/cgo/mmap.go new file mode 100644 index 0000000..144af2b --- /dev/null +++ b/src/runtime/cgo/mmap.go @@ -0,0 +1,31 @@ +// 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 && (amd64 || arm64 || loong64)) || (freebsd && amd64) + +package cgo + +// Import "unsafe" because we use go:linkname. +import _ "unsafe" + +// When using cgo, call the C library for mmap, so that we call into +// any sanitizer interceptors. This supports using the memory +// sanitizer with Go programs. The memory sanitizer only applies to +// C/C++ code; this permits that code to see the Go code as normal +// program addresses that have been initialized. + +// To support interceptors that look for both mmap and munmap, +// also call the C library for munmap. + +//go:cgo_import_static x_cgo_mmap +//go:linkname x_cgo_mmap x_cgo_mmap +//go:linkname _cgo_mmap _cgo_mmap +var x_cgo_mmap byte +var _cgo_mmap = &x_cgo_mmap + +//go:cgo_import_static x_cgo_munmap +//go:linkname x_cgo_munmap x_cgo_munmap +//go:linkname _cgo_munmap _cgo_munmap +var x_cgo_munmap byte +var _cgo_munmap = &x_cgo_munmap diff --git a/src/runtime/cgo/netbsd.go b/src/runtime/cgo/netbsd.go new file mode 100644 index 0000000..8a8018b --- /dev/null +++ b/src/runtime/cgo/netbsd.go @@ -0,0 +1,21 @@ +// 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 netbsd + +package cgo + +import _ "unsafe" // for go:linkname + +// Supply environ and __progname, because we don't +// link against the standard NetBSD crt0.o and the +// libc dynamic library needs them. + +//go:linkname _environ environ +//go:linkname _progname __progname +//go:linkname ___ps_strings __ps_strings + +var _environ uintptr +var _progname uintptr +var ___ps_strings uintptr diff --git a/src/runtime/cgo/openbsd.go b/src/runtime/cgo/openbsd.go new file mode 100644 index 0000000..26b62fb --- /dev/null +++ b/src/runtime/cgo/openbsd.go @@ -0,0 +1,21 @@ +// 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 openbsd + +package cgo + +import _ "unsafe" // for go:linkname + +// Supply __guard_local because we don't link against the standard +// OpenBSD crt0.o and the libc dynamic library needs it. + +//go:linkname _guard_local __guard_local + +var _guard_local uintptr + +// This is normally marked as hidden and placed in the +// .openbsd.randomdata section. +// +//go:cgo_export_dynamic __guard_local __guard_local diff --git a/src/runtime/cgo/setenv.go b/src/runtime/cgo/setenv.go new file mode 100644 index 0000000..2247cb2 --- /dev/null +++ b/src/runtime/cgo/setenv.go @@ -0,0 +1,21 @@ +// 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 unix + +package cgo + +import _ "unsafe" // for go:linkname + +//go:cgo_import_static x_cgo_setenv +//go:linkname x_cgo_setenv x_cgo_setenv +//go:linkname _cgo_setenv runtime._cgo_setenv +var x_cgo_setenv byte +var _cgo_setenv = &x_cgo_setenv + +//go:cgo_import_static x_cgo_unsetenv +//go:linkname x_cgo_unsetenv x_cgo_unsetenv +//go:linkname _cgo_unsetenv runtime._cgo_unsetenv +var x_cgo_unsetenv byte +var _cgo_unsetenv = &x_cgo_unsetenv diff --git a/src/runtime/cgo/sigaction.go b/src/runtime/cgo/sigaction.go new file mode 100644 index 0000000..dc714f7 --- /dev/null +++ b/src/runtime/cgo/sigaction.go @@ -0,0 +1,22 @@ +// 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 && amd64) || (freebsd && amd64) || (linux && arm64) || (linux && ppc64le) + +package cgo + +// Import "unsafe" because we use go:linkname. +import _ "unsafe" + +// When using cgo, call the C library for sigaction, so that we call into +// any sanitizer interceptors. This supports using the sanitizers +// with Go programs. The thread and memory sanitizers only apply to +// C/C++ code; this permits that code to see the Go runtime's existing signal +// handlers when registering new signal handlers for the process. + +//go:cgo_import_static x_cgo_sigaction +//go:linkname x_cgo_sigaction x_cgo_sigaction +//go:linkname _cgo_sigaction _cgo_sigaction +var x_cgo_sigaction byte +var _cgo_sigaction = &x_cgo_sigaction diff --git a/src/runtime/cgo/signal_ios_arm64.go b/src/runtime/cgo/signal_ios_arm64.go new file mode 100644 index 0000000..3425c44 --- /dev/null +++ b/src/runtime/cgo/signal_ios_arm64.go @@ -0,0 +1,10 @@ +// 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. + +package cgo + +import _ "unsafe" + +//go:cgo_export_static xx_cgo_panicmem xx_cgo_panicmem +func xx_cgo_panicmem() diff --git a/src/runtime/cgo/signal_ios_arm64.s b/src/runtime/cgo/signal_ios_arm64.s new file mode 100644 index 0000000..1ae00d1 --- /dev/null +++ b/src/runtime/cgo/signal_ios_arm64.s @@ -0,0 +1,56 @@ +// 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" + +// xx_cgo_panicmem is the entrypoint for SIGSEGV as intercepted via a +// mach thread port as EXC_BAD_ACCESS. As the segfault may have happened +// in C code, we first need to load_g then call xx_cgo_panicmem. +// +// R1 - LR at moment of fault +// R2 - PC at moment of fault +TEXT xx_cgo_panicmem(SB),NOSPLIT|NOFRAME,$0 + // If in external C code, we need to load the g register. + BL runtime·load_g(SB) + CMP $0, g + BNE ongothread + + // On a foreign thread. + // TODO(crawshaw): call badsignal + MOVD.W $0, -16(RSP) + MOVW $139, R1 + MOVW R1, 8(RSP) + B runtime·exit(SB) + +ongothread: + // Trigger a SIGSEGV panic. + // + // The goal is to arrange the stack so it looks like the runtime + // function sigpanic was called from the PC that faulted. It has + // to be sigpanic, as the stack unwinding code in traceback.go + // looks explicitly for it. + // + // To do this we call into runtime·setsigsegv, which sets the + // appropriate state inside the g object. We give it the faulting + // PC on the stack, then put it in the LR before calling sigpanic. + + // Build a 32-byte stack frame for us for this call. + // Saved LR (none available) is at the bottom, + // then the PC argument for setsigsegv, + // then a copy of the LR for us to restore. + MOVD.W $0, -32(RSP) + MOVD R1, 8(RSP) + MOVD R2, 16(RSP) + BL runtime·setsigsegv(SB) + MOVD 8(RSP), R1 + MOVD 16(RSP), R2 + + // Build a 16-byte stack frame for the simulated + // call to sigpanic, by taking 16 bytes away from the + // 32-byte stack frame above. + // The saved LR in this frame is the LR at time of fault, + // and the LR on entry to sigpanic is the PC at time of fault. + MOVD.W R1, 16(RSP) + MOVD R2, R30 + B runtime·sigpanic(SB) |