internal/fakecgo: preserve callee-saved registers in threadentry_trampoline (#347)
`threadentry_trampoline` is called from glibc (that is, C code) when creating a new thread. When the thread exists, `threadentry_trampoline` return back to glibc, which can perform some additional cleanups. We currently aren't honoring the Linux C ABI. It specify some registers as calle-saved which are clobbered by the Go code. This can cause unexpected behaviors when returning to glibc when the thread exists, as the C compiler assumes some registers will have the same value as before the `threadentry_trampoline` function was called, but they don't. This is a precondition to merge #344. Also, might fix #304.
- commit
- 4ad96f670df5c42b56d877115e398c3bc1d2c75d
- author
- Quim Muntal · · 2025-10-16 19:58 UTC
- committer
- GitHub · · 2025-10-16 19:58 UTC
- parent
- 91e14e1
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown
3 files · +45 -4
M internal/fakecgo/trampolines_amd64.s +11 -1
| @@ -25,6 +25,7 @@ return value will be in AX | |||
| 25 | 25 | */ | |
| 26 | 26 | #include "textflag.h" | |
| 27 | 27 | #include "go_asm.h" | |
| 28 | + | #include "abi_amd64.h" | |
| 28 | 29 | ||
| 29 | 30 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |
| 30 | 31 | ||
| @@ -73,11 +74,20 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |||
| 73 | 74 | CALL BX | |
| 74 | 75 | RET | |
| 75 | 76 | ||
| 76 | - | TEXT threadentry_trampoline(SB), NOSPLIT, $16 | |
| 77 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $0 | |
| 78 | + | // See crosscall2. | |
| 79 | + | PUSH_REGS_HOST_TO_ABI0() | |
| 80 | + | ||
| 81 | + | // X15 is designated by Go as a fixed zero register. | |
| 82 | + | // Calling directly into ABIInternal, ensure it is zero. | |
| 83 | + | PXOR X15, X15 | |
| 84 | + | ||
| 77 | 85 | MOVQ DI, AX | |
| 78 | 86 | MOVQ ·threadentry_call(SB), DX | |
| 79 | 87 | MOVQ (DX), CX | |
| 80 | 88 | CALL CX | |
| 89 | + | ||
| 90 | + | POP_REGS_HOST_TO_ABI0() | |
| 81 | 91 | RET | |
| 82 | 92 | ||
| 83 | 93 | TEXT ·call5(SB), NOSPLIT, $0-56 | |
| @@ -25,6 +25,7 @@ return value will be in AX | |||||
| 25 | */ | 25 | */ | ||
| 26 | #include "textflag.h" | 26 | #include "textflag.h" | ||
| 27 | #include "go_asm.h" | 27 | #include "go_asm.h" | ||
| 28 | + | #include "abi_amd64.h" | |||
| 28 | 29 | ||||
| 29 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | 30 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | ||
| 30 | 31 | ||||
| @@ -73,11 +74,20 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |||||
| 73 | CALL BX | 74 | CALL BX | ||
| 74 | RET | 75 | RET | ||
| 75 | 76 | ||||
| 76 | - | TEXT threadentry_trampoline(SB), NOSPLIT, $16 | 77 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $0 |
| 78 | + | // See crosscall2. | |||
| 79 | + | PUSH_REGS_HOST_TO_ABI0() | |||
| 80 | + | ||||
| 81 | + | // X15 is designated by Go as a fixed zero register. | |||
| 82 | + | // Calling directly into ABIInternal, ensure it is zero. | |||
| 83 | + | PXOR X15, X15 | |||
| 84 | + | ||||
| 77 | MOVQ DI, AX | 85 | MOVQ DI, AX | ||
| 78 | MOVQ ·threadentry_call(SB), DX | 86 | MOVQ ·threadentry_call(SB), DX | ||
| 79 | MOVQ (DX), CX | 87 | MOVQ (DX), CX | ||
| 80 | CALL CX | 88 | CALL CX | ||
| 89 | + | ||||
| 90 | + | POP_REGS_HOST_TO_ABI0() | |||
| 81 | RET | 91 | RET | ||
| 82 | 92 | ||||
| 83 | TEXT ·call5(SB), NOSPLIT, $0-56 | 93 | TEXT ·call5(SB), NOSPLIT, $0-56 | ||
M internal/fakecgo/trampolines_arm64.s +16 -1
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | 6 | #include "textflag.h" | |
| 7 | 7 | #include "go_asm.h" | |
| 8 | + | #include "abi_arm64.h" | |
| 8 | 9 | ||
| 9 | 10 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |
| 10 | 11 | ||
| @@ -53,11 +54,25 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |||
| 53 | 54 | RET | |
| 54 | 55 | ||
| 55 | 56 | TEXT threadentry_trampoline(SB), NOSPLIT, $0-0 | |
| 56 | - | MOVD R0, 8(RSP) | |
| 57 | + | // See crosscall2. | |
| 58 | + | SUB $(8*24), RSP | |
| 59 | + | STP (R0, R1), (8*1)(RSP) | |
| 60 | + | MOVD R3, (8*3)(RSP) | |
| 61 | + | ||
| 62 | + | SAVE_R19_TO_R28(8*4) | |
| 63 | + | SAVE_F8_TO_F15(8*14) | |
| 64 | + | STP (R29, R30), (8*22)(RSP) | |
| 65 | + | ||
| 57 | 66 | MOVD ·threadentry_call(SB), R26 | |
| 58 | 67 | MOVD (R26), R2 | |
| 59 | 68 | CALL (R2) | |
| 60 | 69 | MOVD $0, R0 // TODO: get the return value from threadentry | |
| 70 | + | ||
| 71 | + | RESTORE_R19_TO_R28(8*4) | |
| 72 | + | RESTORE_F8_TO_F15(8*14) | |
| 73 | + | LDP (8*22)(RSP), (R29, R30) | |
| 74 | + | ||
| 75 | + | ADD $(8*24), RSP | |
| 61 | 76 | RET | |
| 62 | 77 | ||
| 63 | 78 | TEXT ·call5(SB), NOSPLIT, $0-0 | |
| @@ -5,6 +5,7 @@ | |||||
| 5 | 5 | ||||
| 6 | #include "textflag.h" | 6 | #include "textflag.h" | ||
| 7 | #include "go_asm.h" | 7 | #include "go_asm.h" | ||
| 8 | + | #include "abi_arm64.h" | |||
| 8 | 9 | ||||
| 9 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | 10 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | ||
| 10 | 11 | ||||
| @@ -53,11 +54,25 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0-16 | |||||
| 53 | RET | 54 | RET | ||
| 54 | 55 | ||||
| 55 | TEXT threadentry_trampoline(SB), NOSPLIT, $0-0 | 56 | TEXT threadentry_trampoline(SB), NOSPLIT, $0-0 | ||
| 56 | - | MOVD R0, 8(RSP) | 57 | + | // See crosscall2. |
| 58 | + | SUB $(8*24), RSP | |||
| 59 | + | STP (R0, R1), (8*1)(RSP) | |||
| 60 | + | MOVD R3, (8*3)(RSP) | |||
| 61 | + | ||||
| 62 | + | SAVE_R19_TO_R28(8*4) | |||
| 63 | + | SAVE_F8_TO_F15(8*14) | |||
| 64 | + | STP (R29, R30), (8*22)(RSP) | |||
| 65 | + | ||||
| 57 | MOVD ·threadentry_call(SB), R26 | 66 | MOVD ·threadentry_call(SB), R26 | ||
| 58 | MOVD (R26), R2 | 67 | MOVD (R26), R2 | ||
| 59 | CALL (R2) | 68 | CALL (R2) | ||
| 60 | MOVD $0, R0 // TODO: get the return value from threadentry | 69 | MOVD $0, R0 // TODO: get the return value from threadentry | ||
| 70 | + | ||||
| 71 | + | RESTORE_R19_TO_R28(8*4) | |||
| 72 | + | RESTORE_F8_TO_F15(8*14) | |||
| 73 | + | LDP (8*22)(RSP), (R29, R30) | |||
| 74 | + | ||||
| 75 | + | ADD $(8*24), RSP | |||
| 61 | RET | 76 | RET | ||
| 62 | 77 | ||||
| 63 | TEXT ·call5(SB), NOSPLIT, $0-0 | 78 | TEXT ·call5(SB), NOSPLIT, $0-0 | ||
M internal/fakecgo/trampolines_loong64.s +18 -2
| @@ -5,6 +5,7 @@ | |||
| 5 | 5 | ||
| 6 | 6 | #include "textflag.h" | |
| 7 | 7 | #include "go_asm.h" | |
| 8 | + | #include "abi_loong64.h" | |
| 8 | 9 | ||
| 9 | 10 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |
| 10 | 11 | ||
| @@ -52,11 +53,26 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0 | |||
| 52 | 53 | CALL (R5) | |
| 53 | 54 | RET | |
| 54 | 55 | ||
| 55 | - | TEXT threadentry_trampoline(SB), NOSPLIT, $16 | |
| 56 | - | MOVV R4, 8(R3) | |
| 56 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $0 | |
| 57 | + | // See crosscall2. | |
| 58 | + | ADDV $(-23*8), R3 | |
| 59 | + | MOVV R4, (1*8)(R3) // fn unsafe.Pointer | |
| 60 | + | MOVV R5, (2*8)(R3) // a unsafe.Pointer | |
| 61 | + | MOVV R7, (3*8)(R3) // ctxt uintptr | |
| 62 | + | ||
| 63 | + | SAVE_R22_TO_R31((4*8)) | |
| 64 | + | SAVE_F24_TO_F31((14*8)) | |
| 65 | + | MOVV R1, (22*8)(R3) | |
| 66 | + | ||
| 57 | 67 | MOVV ·threadentry_call(SB), R5 | |
| 58 | 68 | MOVV (R5), R6 | |
| 59 | 69 | CALL (R6) | |
| 70 | + | ||
| 71 | + | RESTORE_R22_TO_R31((4*8)) | |
| 72 | + | RESTORE_F24_TO_F31((14*8)) | |
| 73 | + | MOVV (22*8)(R3), R1 | |
| 74 | + | ||
| 75 | + | ADDV $(23*8), R3 | |
| 60 | 76 | RET | |
| 61 | 77 | ||
| 62 | 78 | TEXT ·call5(SB), NOSPLIT, $0-0 | |
| @@ -5,6 +5,7 @@ | |||||
| 5 | 5 | ||||
| 6 | #include "textflag.h" | 6 | #include "textflag.h" | ||
| 7 | #include "go_asm.h" | 7 | #include "go_asm.h" | ||
| 8 | + | #include "abi_loong64.h" | |||
| 8 | 9 | ||||
| 9 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | 10 | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | ||
| 10 | 11 | ||||
| @@ -52,11 +53,26 @@ TEXT ·setg_trampoline(SB), NOSPLIT, $0 | |||||
| 52 | CALL (R5) | 53 | CALL (R5) | ||
| 53 | RET | 54 | RET | ||
| 54 | 55 | ||||
| 55 | - | TEXT threadentry_trampoline(SB), NOSPLIT, $16 | 56 | + | TEXT threadentry_trampoline(SB), NOSPLIT, $0 |
| 56 | - | MOVV R4, 8(R3) | 57 | + | // See crosscall2. |
| 58 | + | ADDV $(-23*8), R3 | |||
| 59 | + | MOVV R4, (1*8)(R3) // fn unsafe.Pointer | |||
| 60 | + | MOVV R5, (2*8)(R3) // a unsafe.Pointer | |||
| 61 | + | MOVV R7, (3*8)(R3) // ctxt uintptr | |||
| 62 | + | ||||
| 63 | + | SAVE_R22_TO_R31((4*8)) | |||
| 64 | + | SAVE_F24_TO_F31((14*8)) | |||
| 65 | + | MOVV R1, (22*8)(R3) | |||
| 66 | + | ||||
| 57 | MOVV ·threadentry_call(SB), R5 | 67 | MOVV ·threadentry_call(SB), R5 | ||
| 58 | MOVV (R5), R6 | 68 | MOVV (R5), R6 | ||
| 59 | CALL (R6) | 69 | CALL (R6) | ||
| 70 | + | ||||
| 71 | + | RESTORE_R22_TO_R31((4*8)) | |||
| 72 | + | RESTORE_F24_TO_F31((14*8)) | |||
| 73 | + | MOVV (22*8)(R3), R1 | |||
| 74 | + | ||||
| 75 | + | ADDV $(23*8), R3 | |||
| 60 | RET | 76 | RET | ||
| 61 | 77 | ||||
| 62 | TEXT ·call5(SB), NOSPLIT, $0-0 | 78 | TEXT ·call5(SB), NOSPLIT, $0-0 | ||