From e64816b0a24d57eb26297f492cf9d59c4fc72972 Mon Sep 17 00:00:00 2001
From: Petr Shumilov
Date: Wed, 23 Oct 2024 20:24:06 +0300
Subject: [PATCH] Add user context manipulation routines for linux x86_64
Signed-off-by: Petr Shumilov
---
CMakeLists.txt | 2 +-
common/common.cmake | 11 +-
common/dl-utils-lite.cpp | 8 ++
common/dl-utils-lite.h | 1 +
common/server/crash-dump.cpp | 2 +-
.../aarch64/context.cpp} | 23 ++--
common/ucontext/darwin/aarch64/context.h | 54 +++++++++
common/ucontext/darwin/x86_64/context.h | 25 ++++
common/ucontext/linux/aarch64/context.h | 26 ++++
common/ucontext/linux/x86_64/context.h | 113 ++++++++++++++++++
common/ucontext/linux/x86_64/defs.h | 101 ++++++++++++++++
common/ucontext/linux/x86_64/getcontext.S | 53 ++++++++
common/ucontext/linux/x86_64/makecontext.cpp | 77 ++++++++++++
common/ucontext/linux/x86_64/setcontext.S | 48 ++++++++
common/ucontext/linux/x86_64/startcontext.S | 30 +++++
common/ucontext/linux/x86_64/swapcontext.S | 86 +++++++++++++
common/ucontext/ucontext-arm.h | 37 ------
common/ucontext/ucontext-portable.h | 76 +++++++++---
server/json-logger.cpp | 16 +--
server/php-queries.cpp | 3 +-
server/php-runner.cpp | 32 +++--
server/php-runner.h | 2 +-
server/signal-handlers.cpp | 18 +--
server/signal-handlers.h | 2 +-
24 files changed, 739 insertions(+), 107 deletions(-)
rename common/ucontext/{ucontext-arm.cpp => darwin/aarch64/context.cpp} (93%)
create mode 100644 common/ucontext/darwin/aarch64/context.h
create mode 100644 common/ucontext/darwin/x86_64/context.h
create mode 100644 common/ucontext/linux/aarch64/context.h
create mode 100644 common/ucontext/linux/x86_64/context.h
create mode 100644 common/ucontext/linux/x86_64/defs.h
create mode 100644 common/ucontext/linux/x86_64/getcontext.S
create mode 100644 common/ucontext/linux/x86_64/makecontext.cpp
create mode 100644 common/ucontext/linux/x86_64/setcontext.S
create mode 100644 common/ucontext/linux/x86_64/startcontext.S
create mode 100644 common/ucontext/linux/x86_64/swapcontext.S
delete mode 100644 common/ucontext/ucontext-arm.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 6a44c5e272..560450ebef 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16)
project(kphp
VERSION 1.0.1
- LANGUAGES CXX
+ LANGUAGES CXX ASM
DESCRIPTION "Compiler for PHP (aka KPHP)"
HOMEPAGE_URL https://github.com/VKCOM/kphp)
diff --git a/common/common.cmake b/common/common.cmake
index 03d167f231..4553af279e 100644
--- a/common/common.cmake
+++ b/common/common.cmake
@@ -35,9 +35,16 @@ prepend(COMMON_TL_METHODS_SOURCES ${COMMON_DIR}/tl/methods/
rwm.cpp
string.cpp)
-if (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
+if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/
- ucontext-arm.cpp)
+ linux/x86_64/startcontext.S
+ linux/x86_64/getcontext.S
+ linux/x86_64/setcontext.S
+ linux/x86_64/swapcontext.S
+ linux/x86_64/makecontext.cpp)
+else()
+ prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/
+ darwin/aarch64/ucontext-arm.cpp)
endif()
set(COMMON_ALL_SOURCES
diff --git a/common/dl-utils-lite.cpp b/common/dl-utils-lite.cpp
index 8648274d6c..0e5acbf3af 100644
--- a/common/dl-utils-lite.cpp
+++ b/common/dl-utils-lite.cpp
@@ -178,6 +178,14 @@ void dl_allow_all_signals () {
dl_passert (err != -1, "failed to allow all signals");
}
+void dl_unblock_signal (int sig) {
+ sigset_t mask;
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ int err = sigprocmask(SIG_UNBLOCK, &mask, nullptr);
+ dl_passert (err != -1, "failed to unblock signal");
+}
+
static void runtime_handler (const int sig, siginfo_t *info __attribute__((unused)), void *ucontext) {
fprintf (stderr, "%s caught, terminating program\n", strsignal(sig));
crash_dump_write(static_cast(ucontext));
diff --git a/common/dl-utils-lite.h b/common/dl-utils-lite.h
index d9edcc9c32..b964e4acb1 100644
--- a/common/dl-utils-lite.h
+++ b/common/dl-utils-lite.h
@@ -23,6 +23,7 @@ void dl_signal (int sig, void (*handler) (int));
void dl_restore_signal_mask ();
void dl_block_all_signals ();
void dl_allow_all_signals ();
+void dl_unblock_signal (int sig);
void dl_print_backtrace(void **trace, int trace_size);
void dl_print_backtrace();
diff --git a/common/server/crash-dump.cpp b/common/server/crash-dump.cpp
index e16943016e..318ebfd1e8 100644
--- a/common/server/crash-dump.cpp
+++ b/common/server/crash-dump.cpp
@@ -11,7 +11,7 @@
#include
#include "common/kprintf.h"
-#include "common/ucontext/ucontext-portable.h"
+#include
struct crash_dump_buffer {
char scratchpad[1024];
diff --git a/common/ucontext/ucontext-arm.cpp b/common/ucontext/darwin/aarch64/context.cpp
similarity index 93%
rename from common/ucontext/ucontext-arm.cpp
rename to common/ucontext/darwin/aarch64/context.cpp
index b5ffb8781d..13c6233bb5 100644
--- a/common/ucontext/ucontext-arm.cpp
+++ b/common/ucontext/darwin/aarch64/context.cpp
@@ -3,11 +3,10 @@
// Copyright (c) 2023 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt
-#include "common/ucontext/ucontext-arm.h"
+#include "context.h"
#include
#include
-#include
#include
enum { SP_OFFSET = 432, PC_OFFSET = 440, PSTATE_OFFSET = 448, FPSIMD_CONTEXT_OFFSET = 464 };
@@ -31,6 +30,8 @@ static_assert(offsetof(libucontext_ucontext, uc_mcontext.sp) == SP_OFFSET, "SP_O
static_assert(offsetof(libucontext_ucontext, uc_mcontext.pc) == PC_OFFSET, "PC_OFFSET is invalid");
static_assert(offsetof(libucontext_ucontext, uc_mcontext.pstate) == PSTATE_OFFSET, "PSTATE_OFFSET is invalid");
+extern "C" int libucontext_setcontext(const libucontext_ucontext *);
+
__attribute__((visibility("hidden"))) void libucontext_trampoline() {
libucontext_ucontext *uc_link = nullptr;
@@ -43,7 +44,7 @@ __attribute__((visibility("hidden"))) void libucontext_trampoline() {
libucontext_setcontext(uc_link);
}
-void libucontext_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc, ...) {
+void portable_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc, ...) {
unsigned long *sp;
unsigned long *regp;
va_list va;
@@ -73,9 +74,9 @@ void libucontext_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc
va_end(va);
}
-asm(".global " NAME(libucontext_getcontext) ";\n"
+asm(".global " NAME(portable_getcontext) ";\n"
".align 2;\n"
- NAME(libucontext_getcontext) ":\n"
+ NAME(portable_getcontext) ":\n"
"str xzr, [x0, #((184) + ((0) * (8)))]\n" // #REG_OFFSET(0)
/* save GPRs */
"stp x0, x1, [x0, #((184) + ((0) * (8)))]\n" // REG_OFFSET(0)
@@ -109,9 +110,9 @@ asm(".global " NAME(libucontext_getcontext) ";\n"
"mov x0, #0\n"
"ret\n");
-asm(".global " NAME(libucontext_setcontext) ";\n"
+asm(".global " NAME(portable_setcontext) ";\n"
".align 2;\n"
- NAME(libucontext_setcontext) ":\n"
+ NAME(portable_setcontext) ":\n"
/* restore GPRs */
"ldp x18, x19, [x0, #((184) + ((18) * (8)))]\n" // REG_OFFSET(18)
"ldp x20, x21, [x0, #((184) + ((20) * (8)))]\n" // REG_OFFSET(20)
@@ -138,9 +139,9 @@ asm(".global " NAME(libucontext_setcontext) ";\n"
/* jump to new PC */
"br x16\n");
-asm(".global " NAME(libucontext_swapcontext) ";\n"
+asm(".global " NAME(portable_swapcontext) ";\n"
".align 2;\n"
- NAME(libucontext_swapcontext) ":\n"
+ NAME(portable_swapcontext) ":\n"
"str xzr, [x0, #((184) + ((0) * (8)))]\n" // REG_OFFSET(0)
/* save GPRs */
"stp x2, x3, [x0, #((184) + ((2) * (8)))]\n" // REG_OFFSET(2)
@@ -175,7 +176,7 @@ asm(".global " NAME(libucontext_swapcontext) ";\n"
"mov x28, x30\n"
/* move x1 to x0 and call setcontext */
"mov x0, x1\n"
- "bl " NAME(libucontext_setcontext) "\n"
+ "bl " NAME(portable_setcontext) "\n"
/* hmm, we came back here try to return */
"mov x30, x28\n"
- "ret\n");
+ "ret\n");
\ No newline at end of file
diff --git a/common/ucontext/darwin/aarch64/context.h b/common/ucontext/darwin/aarch64/context.h
new file mode 100644
index 0000000000..b12341c1cd
--- /dev/null
+++ b/common/ucontext/darwin/aarch64/context.h
@@ -0,0 +1,54 @@
+// Compiler for PHP (aka KPHP)
+// Copyright (c) 2018-2022 Ariadne Conill
+// https://github.com/kaniini/libucontext/tree/master (copied as third-party and slightly modified)
+// Copyright (c) 2023 LLC «V Kontakte»
+// Distributed under the GPL v3 License, see LICENSE.notice.txt
+
+#pragma once
+
+#include
+
+struct libucontext_mcontext {
+ unsigned long fault_address;
+ unsigned long regs[29];
+ unsigned long fp, lr, sp, pc, pstate;
+ long double __reserved[256];
+};
+
+struct libucontext_stack {
+ void *ss_sp;
+ int ss_flags;
+ size_t ss_size;
+};
+
+struct libucontext_ucontext {
+ unsigned long uc_flags;
+ struct libucontext_ucontext *uc_link;
+ libucontext_stack uc_stack;
+ unsigned char __pad[136];
+ libucontext_mcontext uc_mcontext;
+};
+
+inline constexpr void *get_context_stack_ptr_portable(const libucontext_ucontext &ctx) noexcept {
+ return ctx.uc_stack.ss_sp;
+}
+
+inline constexpr size_t get_context_stack_size_portable(const libucontext_ucontext &ctx) noexcept {
+ return ctx.uc_stack.ss_size;
+}
+
+inline constexpr void set_context_stack_ptr_portable(libucontext_ucontext &ctx, void *sp) noexcept {
+ ctx.uc_stack.ss_sp = sp;
+}
+
+inline constexpr void set_context_stack_size_portable(libucontext_ucontext &ctx, size_t size) noexcept {
+ ctx.uc_stack.ss_size = size;
+}
+
+inline constexpr void set_context_link_portable(libucontext_ucontext &ctx, libucontext_ucontext *link) noexcept {
+ ctx.uc_link = link;
+}
+
+inline void *get_context_stack_base_ptr_portable(const libucontext_ucontext &ctx) noexcept {
+ return reinterpret_cast(ctx.uc_mcontext.fp);
+}
\ No newline at end of file
diff --git a/common/ucontext/darwin/x86_64/context.h b/common/ucontext/darwin/x86_64/context.h
new file mode 100644
index 0000000000..3f771e74a9
--- /dev/null
+++ b/common/ucontext/darwin/x86_64/context.h
@@ -0,0 +1,25 @@
+#include
+
+inline constexpr void *get_context_stack_ptr_portable(const ucontext_t &ctx) noexcept {
+ return ctx.uc_stack.ss_sp;
+}
+
+inline constexpr size_t get_context_stack_size_portable(const ucontext_t &ctx) noexcept {
+ return ctx.uc_stack.ss_size;
+}
+
+inline constexpr void set_context_stack_ptr_portable(ucontext_t &ctx, void *sp) noexcept {
+ ctx.uc_stack.ss_sp = sp;
+}
+
+inline constexpr void set_context_stack_size_portable(ucontext_t &ctx, size_t size) noexcept {
+ ctx.uc_stack.ss_size = size;
+}
+
+inline constexpr void set_context_link_portable(ucontext_t &ctx, ucontext_t *link) noexcept {
+ ctx.uc_link = link;
+}
+
+inline void *get_context_stack_base_ptr_portable(const ucontext_t &ctx) noexcept {
+ return reinterpret_cast(ctx.uc_mcontext->__ss.__rbp);
+}
\ No newline at end of file
diff --git a/common/ucontext/linux/aarch64/context.h b/common/ucontext/linux/aarch64/context.h
new file mode 100644
index 0000000000..71b290cefa
--- /dev/null
+++ b/common/ucontext/linux/aarch64/context.h
@@ -0,0 +1,26 @@
+#include
+
+inline constexpr void *get_context_stack_ptr_portable(const ucontext_t &ctx) noexcept {
+ return ctx.uc_stack.ss_sp;
+}
+
+inline constexpr size_t get_context_stack_size_portable(const ucontext_t &ctx) noexcept {
+ return ctx.uc_stack.ss_size;
+}
+
+inline constexpr void set_context_stack_ptr_portable(ucontext_t &ctx, void *sp) noexcept {
+ ctx.uc_stack.ss_sp = sp;
+}
+
+inline constexpr void set_context_stack_size_portable(ucontext_t &ctx, size_t size) noexcept {
+ ctx.uc_stack.ss_size = size;
+}
+
+inline constexpr void set_context_link_portable(ucontext_t &ctx, ucontext_t *link) noexcept {
+ ctx.uc_link = link;
+}
+
+inline void *get_context_stack_base_ptr_portable(const ucontext_t &ctx) noexcept {
+ // 29 -- magic constant, based on libc implementation from `sysdeps/unix/sysv/linux/aarch64/ucontext-internal.h`
+ return reinterpret_cast(ctx.uc_mcontext.regs[29]);
+}
\ No newline at end of file
diff --git a/common/ucontext/linux/x86_64/context.h b/common/ucontext/linux/x86_64/context.h
new file mode 100644
index 0000000000..7801b76f2d
--- /dev/null
+++ b/common/ucontext/linux/x86_64/context.h
@@ -0,0 +1,113 @@
+#include
+#include
+#include "defs.h"
+
+#pragma once
+
+// Type for general register
+using kgreg_t = uint64_t;
+
+// Container for all general registers
+using kgregset_t = kgreg_t[NUM_GENERAL_REG];
+
+// Prohibit packing
+#pragma pack(push, 1)
+
+struct kfpxreg {
+ uint16_t significand[4];
+ uint16_t exponent;
+ uint16_t reserved1[3];
+};
+
+struct kxmmreg {
+ uint32_t element[4];
+};
+
+struct kfpstate {
+ uint16_t cwd;
+ uint16_t swd;
+ uint16_t ftw;
+ uint16_t fop;
+ uint16_t rip;
+ uint16_t rdp;
+ uint16_t mxcsr;
+ uint16_t mxcr_mask;
+ kfpxreg st[8];
+ kxmmreg xmm[16];
+ uint32_t reserved1[24];
+};
+
+// Structure to describe FPU registers
+using kfpregset_t = kfpstate *;
+
+// Context to describe whole processor state
+struct kmcontext_t {
+ kgregset_t gregs;
+ // Note that fpregs is a pointer
+ kfpregset_t fpregs;
+ uint64_t reserved1[8];
+};
+
+struct kstack_t {
+ void *sp;
+ uint64_t size;
+};
+
+// Userlevel KPHP context
+struct kcontext_t {
+ uint64_t flags;
+ kcontext_t *link;
+ kstack_t stack;
+ kmcontext_t mcontext;
+ kfpstate fpregs_mem;
+};
+
+#pragma pack(pop)
+
+inline constexpr void *get_context_stack_ptr_portable(const kcontext_t &ctx) noexcept {
+ return ctx.stack.sp;
+}
+
+inline constexpr uint64_t get_context_stack_size_portable(const kcontext_t &ctx) noexcept {
+ return ctx.stack.size;
+}
+
+inline constexpr void set_context_stack_ptr_portable(kcontext_t &ctx, void *sp) noexcept {
+ ctx.stack.sp = sp;
+}
+
+inline constexpr void set_context_stack_size_portable(kcontext_t &ctx, uint64_t size) noexcept {
+ ctx.stack.size = size;
+}
+
+inline constexpr void set_context_link_portable(kcontext_t &ctx, kcontext_t *link) noexcept {
+ ctx.link = link;
+}
+
+inline void *get_context_stack_base_ptr_portable(const kcontext_t &ctx) noexcept {
+ return reinterpret_cast(ctx.mcontext.gregs[GREG_RBP]);
+}
+
+// Zero-cost offsets double-checking
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RBP]) == oRBP, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RSP]) == oRSP, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RBX]) == oRBX, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R8]) == oR8, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R9]) == oR9, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R10]) == oR10, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R11]) == oR11, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R12]) == oR12, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R13]) == oR13, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R14]) == oR14, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R15]) == oR15, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RDI]) == oRDI, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RSI]) == oRSI, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RDX]) == oRDX, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RAX]) == oRAX, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RCX]) == oRCX, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RIP]) == oRIP, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_EFL]) == oEFL, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.gregs) == KCONTEXT_MCONTEXT_GREGS, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, mcontext.fpregs) == KCONTEXT_MCONTEXT_FPREGS, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, fpregs_mem) == KCONTEXT_FPREGS_MEM, "Invalid offset assumption");
+static_assert(offsetof(kcontext_t, fpregs_mem.mxcsr) == KCONTEXT_FPREGS_MEM_MXCSR, "Invalid offset assumption");
diff --git a/common/ucontext/linux/x86_64/defs.h b/common/ucontext/linux/x86_64/defs.h
new file mode 100644
index 0000000000..ec0c0449c5
--- /dev/null
+++ b/common/ucontext/linux/x86_64/defs.h
@@ -0,0 +1,101 @@
+#ifndef TYPE
+# ifdef __clang__
+# define TYPE(name) // .type not supported
+# else
+# define TYPE(name) .type name, @function;
+# endif
+#endif
+
+#define ALIGNARG(log2) 1 << log2
+
+// Define an entry point visible from C/C++.
+#define ENTRY_P2ALIGN(name, alignment) \
+ .globl name; \
+ TYPE(name) \
+ .align ALIGNARG(alignment); \
+ name:
+
+// Common entry 16 byte aligns
+#define ENTRY(name) ENTRY_P2ALIGN(name, 4)
+#define END(name) .size name, .- name;
+
+// Number of general registers
+#define NUM_GENERAL_REG 23
+
+// Register size
+#define GREG_SIZE 8
+
+// General registers
+#define GREG_R8 0
+#define GREG_R9 1
+#define GREG_R10 2
+#define GREG_R11 3
+#define GREG_R12 4
+#define GREG_R13 5
+#define GREG_R14 6
+#define GREG_R15 7
+#define GREG_RDI 8
+#define GREG_RSI 9
+#define GREG_RBP 10
+#define GREG_RBX 11
+#define GREG_RDX 12
+#define GREG_RAX 13
+#define GREG_RCX 14
+#define GREG_RSP 15
+#define GREG_RIP 16
+#define GREG_EFL 17
+#define GREG_CSGSFS 18
+#define GREG_ERR 19
+#define GREG_TRAPNO 20
+#define GREG_OLDMASK 21
+#define GREG_CR2 22
+
+// KPHP user context layout
+
+/* struct kcontext_t { OFFSET*/
+/* uint64_t flags 0 */
+/* kcontext_t* link 8 */
+/* kstack_t stack 16 */
+/* struct kmcontext_t mcontext { */
+/* kgregset_t gregs 32 */ #define KCONTEXT_MCONTEXT_GREGS (32)
+/* kfpregset_t fpregs 216 */ #define KCONTEXT_MCONTEXT_FPREGS (KCONTEXT_MCONTEXT_GREGS + (GREG_SIZE * NUM_GENERAL_REG))
+/* uint64_t reserved1[8] 224 */
+/* } */
+/* struct kfpstate fpregs_mem { */
+/* uint16_t cwd 288 */ #define KCONTEXT_FPREGS_MEM (KCONTEXT_MCONTEXT_FPREGS + 8 + (8 * 8))
+/* uint16_t swd 290 */
+/* uint16_t ftw 292 */
+/* uint16_t fop 294 */
+/* uint16_t rip 296 */
+/* uint16_t rdp 298 */
+/* uint16_t mxcsr 300 */ #define KCONTEXT_FPREGS_MEM_MXCSR (KCONTEXT_FPREGS_MEM + (2 * 6))
+/* uint16_t mxcr_mask */
+/* kfpxreg st[8] */
+/* kxmmreg xmm[16] */
+/* uint32_t reserved1[24] */
+/* } */
+/* } */
+
+// Offset of register inside context
+#define KCONTEXT_MCONTEXT_GREG_OFFSET(reg) (KCONTEXT_MCONTEXT_GREGS + ((reg) * GREG_SIZE))
+#define oRBP KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RBP)
+#define oRSP KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RSP)
+#define oRBX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RBX)
+#define oR8 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R8)
+#define oR9 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R9)
+#define oR10 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R10)
+#define oR11 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R11)
+#define oR12 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R12)
+#define oR13 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R13)
+#define oR14 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R14)
+#define oR15 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R15)
+#define oRDI KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RDI)
+#define oRSI KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RSI)
+#define oRDX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RDX)
+#define oRAX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RAX)
+#define oRCX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RCX)
+#define oRIP KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RIP)
+#define oEFL KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_EFL)
+#define oFPREGS KCONTEXT_MCONTEXT_FPREGS
+#define oFPREGSMEM KCONTEXT_FPREGS_MEM
+#define oMXCSR KCONTEXT_FPREGS_MEM_MXCSR
\ No newline at end of file
diff --git a/common/ucontext/linux/x86_64/getcontext.S b/common/ucontext/linux/x86_64/getcontext.S
new file mode 100644
index 0000000000..1baed5248e
--- /dev/null
+++ b/common/ucontext/linux/x86_64/getcontext.S
@@ -0,0 +1,53 @@
+#include "defs.h"
+
+/*
+ * int getcontext_portable(kcontext_t *kcp)
+
+ Stores a user context in the same manner as in libc but without signals state storing.
+ Main goal of getting rid of manipulation with signals state -- remove syscall and performance gain.
+ IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE!
+
+ In any case returns 0
+*/
+
+ENTRY(getcontext_portable)
+ // Save the registers, the registers used for passing args, and the return address
+ movq %rbx, oRBX(%rdi)
+ movq %rbp, oRBP(%rdi)
+ movq %r12, oR12(%rdi)
+ movq %r13, oR13(%rdi)
+ movq %r14, oR14(%rdi)
+ movq %r15, oR15(%rdi)
+ movq %rdi, oRDI(%rdi)
+ movq %rsi, oRSI(%rdi)
+ movq %rdx, oRDX(%rdi)
+ movq %rcx, oRCX(%rdi)
+ movq %r8, oR8(%rdi)
+ movq %r9, oR9(%rdi)
+ /*
+ %rsp -> +-----------------------+ / \
+ | Return address | |
+ 8(%rsp) -> +-----------------------+ |
+ | Caller local var #N | |
+ +-----------------------+ |
+ | ... | |
+ +-----------------------+ 0xFFFFFFFF
+ */
+ movq (%rsp), %rcx
+ movq %rcx, oRIP(%rdi)
+ leaq 8(%rsp), %rcx
+ movq %rcx, oRSP(%rdi)
+
+ // We have separate floating-point register content memory on the
+ // stack. We use the fpregs_mem block in the context
+ leaq oFPREGSMEM(%rdi), %rcx
+ movq %rcx, oFPREGS(%rdi)
+ // Save the floating-point environment
+ fnstenv (%rcx)
+ fldenv (%rcx)
+ stmxcsr oMXCSR(%rdi)
+
+ // All done, return 0 for success
+ xorl %eax, %eax
+ ret
+END(getcontext_portable)
\ No newline at end of file
diff --git a/common/ucontext/linux/x86_64/makecontext.cpp b/common/ucontext/linux/x86_64/makecontext.cpp
new file mode 100644
index 0000000000..3f86820a92
--- /dev/null
+++ b/common/ucontext/linux/x86_64/makecontext.cpp
@@ -0,0 +1,77 @@
+#include "context.h"
+#include
+#include
+
+/* This implementation can handle any ARGC value but only
+ normal integer parameters.
+ makecontext sets up a stack and the registers for the
+ user context. The stack looks like this:
+
+ %rsp -> +-----------------------+
+ | trampoline address | / \
+ +-----------------------+ |
+ | parameter 7-n | |
+ +-----------------------+ |
+ | next context | |
+ +-----------------------+ stack.sp + stack.size
+
+ The registers are set up like this:
+ %rdi,%rsi,%rdx,%rcx,%r8,%r9: parameter 1 to 6
+ %rbx : address of next context
+ %rsp : stack pointer.
+*/
+
+extern "C" void start_context_portable(void) __attribute__((visibility("hidden")));
+
+extern "C" void makecontext_portable(kcontext_t *kcp, void (*func)(void), int argc, ...) {
+ kgreg_t *sp;
+ va_list ap;
+ uint32_t argc_on_stack = (argc > 6 ? argc - 6 : 0); // Prev 6 args passed via regs
+ uint32_t link = argc_on_stack + 1;
+ int i;
+
+ // Generate room on stack for parameter if needed and link
+ sp = reinterpret_cast(reinterpret_cast(kcp->stack.sp) + kcp->stack.size);
+ sp -= argc_on_stack + 1;
+ // Align stack and make space for trampoline address
+ sp = reinterpret_cast((reinterpret_cast(sp) & -16L) - 8);
+
+ // Address to jump to context entrypoint
+ kcp->mcontext.gregs[GREG_RIP] = reinterpret_cast(func);
+ // Setup rbx
+ kcp->mcontext.gregs[GREG_RBX] = reinterpret_cast(&sp[link]);
+ kcp->mcontext.gregs[GREG_RSP] = reinterpret_cast(sp);
+
+ /* Setup stack. */
+ sp[0] = reinterpret_cast(&start_context_portable);
+ sp[link] = reinterpret_cast(kcp->link);
+
+ va_start(ap, argc);
+ /* Handle arguments. */
+ for (i = 0; i < argc; ++i)
+ switch (i) {
+ case 0:
+ kcp->mcontext.gregs[GREG_RDI] = va_arg(ap, kgreg_t);
+ break;
+ case 1:
+ kcp->mcontext.gregs[GREG_RSI] = va_arg(ap, kgreg_t);
+ break;
+ case 2:
+ kcp->mcontext.gregs[GREG_RDX] = va_arg(ap, kgreg_t);
+ break;
+ case 3:
+ kcp->mcontext.gregs[GREG_RCX] = va_arg(ap, kgreg_t);
+ break;
+ case 4:
+ kcp->mcontext.gregs[GREG_R8] = va_arg(ap, kgreg_t);
+ break;
+ case 5:
+ kcp->mcontext.gregs[GREG_R9] = va_arg(ap, kgreg_t);
+ break;
+ default:
+ /* Put value on stack. */
+ sp[i - 5] = va_arg(ap, kgreg_t);
+ break;
+ }
+ va_end(ap);
+}
\ No newline at end of file
diff --git a/common/ucontext/linux/x86_64/setcontext.S b/common/ucontext/linux/x86_64/setcontext.S
new file mode 100644
index 0000000000..975ecd9553
--- /dev/null
+++ b/common/ucontext/linux/x86_64/setcontext.S
@@ -0,0 +1,48 @@
+#include "defs.h"
+
+/*
+ * int setcontext_portable(kcontext_t *kcp)
+
+ Restores a user context in the same manner as in libc but without signals state restoring.
+ Main goal of getting rid of manipulation with signals state -- remove syscall and performance gain.
+ IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE!
+
+ In any case returns 0
+*/
+
+ENTRY(setcontext_portable)
+ // Move the pointer into RDX. The choice is arbitrary, but
+ // leaving RDI and RSI available for use later can avoid
+ // shuffling values
+ movq %rdi, %rdx
+
+ // Restore the floating-point context
+ movq oFPREGS(%rdx), %rcx
+ fldenv (%rcx)
+ ldmxcsr oMXCSR(%rdx)
+
+ // Load the new stack pointer, the preserved registers and registers used for passing args
+ movq oRSP(%rdx), %rsp
+ movq oRBX(%rdx), %rbx
+ movq oRBP(%rdx), %rbp
+ movq oR12(%rdx), %r12
+ movq oR13(%rdx), %r13
+ movq oR14(%rdx), %r14
+ movq oR15(%rdx), %r15
+
+ // The following ret should return to the address set with getcontext. Therefore, push the address on the stack
+ movq oRIP(%rdx), %rcx
+ pushq %rcx
+
+ movq oRSI(%rdx), %rsi
+ movq oRDI(%rdx), %rdi
+ movq oRCX(%rdx), %rcx
+ movq oR8(%rdx), %r8
+ movq oR9(%rdx), %r9
+ movq oRDX(%rdx), %rdx
+
+ // All done, return 0 for success
+ xorl %eax, %eax
+ ret
+END(setcontext_portable)
+
diff --git a/common/ucontext/linux/x86_64/startcontext.S b/common/ucontext/linux/x86_64/startcontext.S
new file mode 100644
index 0000000000..01db662596
--- /dev/null
+++ b/common/ucontext/linux/x86_64/startcontext.S
@@ -0,0 +1,30 @@
+#include "defs.h"
+
+/*
+ * void start_context_portable()
+ The trampoline for a `next` context which is located in `link` of kcontext_t.
+ It is placed into user-created stack by `makecontext`.
+
+ %rsp -> +---------------------------+
+ | &start_context_portable | / \
+ +---------------------------+ |
+ | entrypoint parameter 7-n | |
+ %rbx -> +---------------------------+ |
+ | next context | |
+ +---------------------------+ stack.sp + stack.size
+*/
+
+ENTRY(start_context_portable)
+ // This removes the parameters passed to the function given to
+ // 'makecontext' from the stack. RBX contains the address
+ // on the stack pointer for the next context. */
+ movq %rbx, %rsp
+
+ // Don't use pop here so that stack is aligned to 16 bytes
+ movq (%rsp), %rdi // This is the next context
+ testq %rdi, %rdi
+ je 2f // If it is null then exit
+ call setcontext_portable
+2:
+ call exit
+END(start_context_portable)
diff --git a/common/ucontext/linux/x86_64/swapcontext.S b/common/ucontext/linux/x86_64/swapcontext.S
new file mode 100644
index 0000000000..633bb767dd
--- /dev/null
+++ b/common/ucontext/linux/x86_64/swapcontext.S
@@ -0,0 +1,86 @@
+#include "defs.h"
+
+/*
+ * int swapcontext_portable(kcontext_t *out_kcp, kcontext_t *kcp)
+
+ Stores a user context into `out_kcp` and restores context from `kcp` in the same manner as in libc.
+ But without signals state storing/restoring.
+ Main goal of getting rid of manipulation with signals state -- remove syscall and performance gain.
+ IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE!
+
+ In any case returns 0
+*/
+
+ENTRY(swapcontext_portable)
+ // Save the preserved registers, the registers used for passing args, and the return address
+ movq %rbx, oRBX(%rdi)
+ movq %rbp, oRBP(%rdi)
+ movq %r12, oR12(%rdi)
+ movq %r13, oR13(%rdi)
+ movq %r14, oR14(%rdi)
+ movq %r15, oR15(%rdi)
+ movq %rdi, oRDI(%rdi)
+ movq %rsi, oRSI(%rdi)
+ movq %rdx, oRDX(%rdi)
+ movq %rcx, oRCX(%rdi)
+ movq %r8, oR8(%rdi)
+ movq %r9, oR9(%rdi)
+ /*
+ %rsp -> +-----------------------+ / \
+ | Return address | |
+ 8(%rsp) -> +-----------------------+ |
+ | Caller local var #N | |
+ +-----------------------+ |
+ | ... | |
+ +-----------------------+ 0xFFFFFFFF
+ */
+ movq (%rsp), %rcx
+ movq %rcx, oRIP(%rdi)
+ leaq 8(%rsp), %rcx
+ movq %rcx, oRSP(%rdi)
+
+ // We have separate floating-point register content memory on the
+ // stack. We use the fpregs_mem block in the context
+ leaq oFPREGSMEM(%rdi), %rcx
+ movq %rcx, oFPREGS(%rdi)
+ // Save the floating-point environment
+ fnstenv (%rcx)
+ stmxcsr oMXCSR(%rdi)
+
+ // Move restored ctx pointer to %rdx
+ movq %rsi, %rdx
+
+ // Restore the floating-point context
+ movq oFPREGS(%rdx), %rcx
+ fldenv (%rcx)
+ ldmxcsr oMXCSR(%rdx)
+
+ // Load the new stack pointer and the preserved registers
+ movq oRSP(%rdx), %rsp
+ movq oRBX(%rdx), %rbx
+ movq oRBP(%rdx), %rbp
+ movq oR12(%rdx), %r12
+ movq oR13(%rdx), %r13
+ movq oR14(%rdx), %r14
+ movq oR15(%rdx), %r15
+
+
+ // The following ret should return to the address set with
+ // getcontext. Therefore. push the address on the stack
+ movq oRIP(%rdx), %rcx
+ pushq %rcx
+
+ // Setup registers used for passing args
+ movq oRDI(%rdx), %rdi
+ movq oRSI(%rdx), %rsi
+ movq oRCX(%rdx), %rcx
+ movq oR8(%rdx), %r8
+ movq oR9(%rdx), %r9
+ movq oRDX(%rdx), %rdx
+
+ // Clear rax to indicate success
+ xorl %eax, %eax
+ ret
+END(swapcontext_portable)
+
+
diff --git a/common/ucontext/ucontext-arm.h b/common/ucontext/ucontext-arm.h
deleted file mode 100644
index fae5b18da6..0000000000
--- a/common/ucontext/ucontext-arm.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Compiler for PHP (aka KPHP)
-// Copyright (c) 2018-2022 Ariadne Conill
-// https://github.com/kaniini/libucontext/tree/master (copied as third-party and slightly modified)
-// Copyright (c) 2023 LLC «V Kontakte»
-// Distributed under the GPL v3 License, see LICENSE.notice.txt
-
-#pragma once
-
-#include
-
-struct libucontext_mcontext {
- unsigned long fault_address;
- unsigned long regs[29];
- unsigned long fp, lr, sp, pc, pstate;
- long double __reserved[256];
-};
-
-struct libucontext_stack {
- void *ss_sp;
- int ss_flags;
- size_t ss_size;
-};
-
-struct libucontext_ucontext {
- unsigned long uc_flags;
- struct libucontext_ucontext *uc_link;
- libucontext_stack uc_stack;
- unsigned char __pad[136];
- libucontext_mcontext uc_mcontext;
-};
-
-extern "C" {
-void libucontext_makecontext(libucontext_ucontext *, void (*)(), int, ...);
-int libucontext_getcontext(libucontext_ucontext *);
-int libucontext_setcontext(const libucontext_ucontext *);
-int libucontext_swapcontext(libucontext_ucontext *, const libucontext_ucontext *);
-}
diff --git a/common/ucontext/ucontext-portable.h b/common/ucontext/ucontext-portable.h
index 864c0f1cee..a5b3c4ac80 100644
--- a/common/ucontext/ucontext-portable.h
+++ b/common/ucontext/ucontext-portable.h
@@ -1,27 +1,65 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2021 LLC «V Kontakte»
// Distributed under the GPL v3 License, see LICENSE.notice.txt
-
#pragma once
+#include
+
#ifdef __x86_64__
-// for x86 mac or x86/arm linux, we just use makecontext(), ucontext_t and other native functions
-#include
-
-#define ucontext_t_portable ucontext_t
-#define setcontext_portable setcontext
-#define getcontext_portable getcontext
-#define makecontext_portable makecontext
-#define swapcontext_portable swapcontext
-
-#else
-// for M1, we can't use native makecontext() and others: they compile, but hang up when called
-#include "common/ucontext/ucontext-arm.h"
-
-#define ucontext_t_portable libucontext_ucontext
-#define setcontext_portable libucontext_setcontext
-#define getcontext_portable libucontext_getcontext
-#define makecontext_portable libucontext_makecontext
-#define swapcontext_portable libucontext_swapcontext
+# ifdef __linux__
+/*
+ *
+ * For X86_64 Linux platform we use own implementation of user context manipulation routines in the same manner as in libc.
+ * But without signals state saving/restoring.
+ * IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE!
+ *
+ */
+# include "linux/x86_64/context.h"
+ using ucontext_t_portable = kcontext_t;
+
+ extern "C" {
+ int getcontext_portable(ucontext_t_portable *);
+ void makecontext_portable(ucontext_t_portable *, void (*)(), int, ...);
+ int setcontext_portable(const ucontext_t_portable *);
+ int swapcontext_portable(ucontext_t_portable *, const ucontext_t_portable *);
+ }
+
+# elif __APPLE__
+# include "darwin/x86_64/context.h"
+ using ucontext_t_portable = ucontext_t;
+
+# define getcontext_portable getcontext
+# define makecontext_portable makecontext
+# define setcontext_portable setcontext
+# define swapcontext_portable swapcontext
+
+# else
+ static_assert(false, "Unsupported OS for x86_64 platform");
+# endif
+
+#elif defined(__aarch64__) || defined(__arm64__)
+
+# ifdef __linux__
+# include "linux/aarch64/context.h"
+ using ucontext_t_portable = ucontext_t;
+
+# define getcontext_portable getcontext
+# define makecontext_portable makecontext
+# define setcontext_portable setcontext
+# define swapcontext_portable swapcontext
+
+# elif __APPLE__
+// For M1, we can't use native makecontext() and others: they compile, but hang up when called
+# include "darwin/aarch64/context.h"
+ using ucontext_t_portable = libucontext_ucontext;
+
+ int getcontext_portable(ucontext_t_portable *);
+ void makecontext_portable(ucontext_t_portable *, void (*)(), int, ...);
+ int setcontext_portable(const ucontext_t_portable *);
+ int swapcontext_portable(ucontext_t_portable *, const ucontext_t_portable *);
+
+# else
+ static_assert(false, "Unsupported OS for aarch64 platform");
+# endif
#endif
diff --git a/server/json-logger.cpp b/server/json-logger.cpp
index d5e7614154..f0a6a6c935 100644
--- a/server/json-logger.cpp
+++ b/server/json-logger.cpp
@@ -11,6 +11,7 @@
#include "common/algorithms/find.h"
#include "common/fast-backtrace.h"
#include "common/wrappers/likely.h"
+#include "common/ucontext/ucontext-portable.h"
#include "runtime/kphp-backtrace.h"
#include "server/server-config.h"
#include "server/json-logger.h"
@@ -81,20 +82,7 @@ int script_backtrace(void **buffer, int size) {
return 0;
}
const ucontext_t_portable &context = PhpScript::current_script->run_context;
-#if defined(__APPLE__)
-#if defined(__arm64__)
- void *rbp = reinterpret_cast(context.uc_mcontext.fp);
-#else
- void *rbp = reinterpret_cast(context.uc_mcontext->__ss.__rbp);
-#endif
-#elif defined(__x86_64__)
- void *rbp = reinterpret_cast(context.uc_mcontext.gregs[REG_RBP]);
-#elif defined(__aarch64__) || defined(__arm64__)
- void *rbp = reinterpret_cast(context.uc_mcontext.fp);
-#else
- void *rbp = nullptr;
- size = 0;
-#endif
+ void *rbp = get_context_stack_base_ptr_portable(context);
char *stack_start = PhpScript::current_script->script_stack.get_stack_ptr();
char *stack_end = stack_start + PhpScript::current_script->script_stack.get_stack_size();
return fast_backtrace_by_bp(rbp, stack_end, buffer, size);
diff --git a/server/php-queries.cpp b/server/php-queries.cpp
index ad3d24d95d..bd18efeaf2 100644
--- a/server/php-queries.cpp
+++ b/server/php-queries.cpp
@@ -9,6 +9,7 @@
#include
#include
#include
+#include
#include
#include "common/precise-time.h"
@@ -1012,7 +1013,7 @@ php_net_query_packet_answer_t *php_net_query_get(int connection_id, const char *
}
void script_error() {
- PhpScript::error("script_error called", script_error_t::unclassified_error);
+ PhpScript::error("script_error called", script_error_t::unclassified_error, /* no triggered by signal */ std::nullopt);
}
void http_set_result(const char *headers, int headers_len, const char *body, int body_len, int exit_code) {
diff --git a/server/php-runner.cpp b/server/php-runner.cpp
index d02d04d3ef..ac6bb497d2 100644
--- a/server/php-runner.cpp
+++ b/server/php-runner.cpp
@@ -4,6 +4,7 @@
#include "server/php-runner.h"
+#include "common/ucontext/ucontext-portable.h"
#include
#include
#include
@@ -80,13 +81,24 @@ void send_slow_net_event_stats(const net_event_t &event, double time_sec) noexce
} // namespace
-void PhpScript::error(const char *error_message, script_error_t error_type) noexcept {
+void PhpScript::error(const char *error_message, script_error_t error_type, const std::optional &triggered_by_signal) noexcept {
assert (in_script_context == true);
in_script_context = false;
current_script->state = run_state_t::error;
current_script->error_message = error_message;
current_script->error_type = error_type;
- stack_end = reinterpret_cast(exit_context.uc_stack.ss_sp) + exit_context.uc_stack.ss_size;
+ stack_end = reinterpret_cast(get_context_stack_ptr_portable(exit_context)) + get_context_stack_size_portable(exit_context);
+
+#if defined(__linux__) && defined(__x86_64__)
+ // The error may be produced in process of signal handling. The default behavior on Linux-based platforms
+ // consider to block a signal during handler execution and unblock after handler ending.
+ // For x86_64 arch we have context replacement implementation where the signals manipulations is omitted by design,
+ // e.g. we do not save signals state in context replacement.
+ // Therefore, we need manual control for signal state, especially, we have to unblock blocked signals at logical end of handler.
+ if (triggered_by_signal.has_value()) {
+ dl_unblock_signal(triggered_by_signal.value());
+ }
+#endif
#if ASAN_ENABLED
__sanitizer_start_switch_fiber(nullptr, main_thread_stack, main_thread_stacksize);
#endif
@@ -103,7 +115,7 @@ void PhpScript::try_run_shutdown_functions_on_timeout() noexcept {
return;
}
if (vk::singleton::get().is_running()) {
- perform_error_if_running("timeout exit in OOM handler\n", script_error_t::timeout);
+ perform_error_if_running("timeout exit in OOM handler\n", script_error_t::timeout, /* no triggered by signal */std::nullopt);
return;
}
@@ -116,14 +128,14 @@ void PhpScript::try_run_shutdown_functions_on_timeout() noexcept {
state = run_state_t::running;
run_shutdown_functions_from_timeout();
}
- perform_error_if_running("timeout exit\n", script_error_t::timeout);
+ perform_error_if_running("timeout exit\n", script_error_t::timeout, /* no triggered by signal */ std::nullopt);
}
void PhpScript::check_net_context_errors() noexcept {
php_assert(PhpScript::in_script_context);
if (memory_limit_exceeded) {
vk::singleton::get().invoke();
- perform_error_if_running("memory limit exit\n", script_error_t::memory_limit);
+ perform_error_if_running("memory limit exit\n", script_error_t::memory_limit, /* no triggered by signal */std::nullopt);
}
try_run_shutdown_functions_on_timeout();
}
@@ -197,9 +209,9 @@ void PhpScript::init(script_t *script, php_query_data_t *data_to_set) noexcept {
assert_state(run_state_t::before_init);
getcontext_portable(&run_context);
- run_context.uc_stack.ss_sp = script_stack.get_stack_ptr();
- run_context.uc_stack.ss_size = script_stack.get_stack_size();
- run_context.uc_link = nullptr;
+ set_context_stack_ptr_portable(run_context, script_stack.get_stack_ptr());
+ set_context_stack_size_portable(run_context, script_stack.get_stack_size());
+ set_context_link_portable(run_context, nullptr);
makecontext_portable(&run_context, &script_context_entrypoint, 0);
run_main = script;
@@ -225,7 +237,7 @@ void PhpScript::init(script_t *script, php_query_data_t *data_to_set) noexcept {
}
int PhpScript::swapcontext_helper(ucontext_t_portable *oucp, const ucontext_t_portable *ucp) {
- stack_end = reinterpret_cast(ucp->uc_stack.ss_sp) + ucp->uc_stack.ss_size;
+ stack_end = reinterpret_cast(get_context_stack_ptr_portable(*ucp)) + get_context_stack_size_portable(*ucp);
return swapcontext_portable(oucp, ucp);
}
@@ -477,7 +489,7 @@ void PhpScript::run() noexcept {
php_assert(CurException.is_null());
CurException = saved_exception;
}
- error(php_uncaught_exception_error(CurException), script_error_t::exception);
+ error(php_uncaught_exception_error(CurException), script_error_t::exception, /* no triggered by signal */ std::nullopt);
}
}
diff --git a/server/php-runner.h b/server/php-runner.h
index ce4394a4f9..cd728b1e16 100644
--- a/server/php-runner.h
+++ b/server/php-runner.h
@@ -130,7 +130,7 @@ class PhpScript {
script_result *res{nullptr};
static void script_context_entrypoint() noexcept;
- static void error(const char *error_message, script_error_t error_type) noexcept;
+ static void error(const char *error_message, script_error_t error_type, const std::optional &triggered_by_signal) noexcept;
PhpScript(size_t mem_size, double oom_handling_memory_ratio, size_t stack_size) noexcept;
~PhpScript() noexcept;
diff --git a/server/signal-handlers.cpp b/server/signal-handlers.cpp
index 79a07fdd80..ea1b2b4d1d 100644
--- a/server/signal-handlers.cpp
+++ b/server/signal-handlers.cpp
@@ -55,7 +55,7 @@ void default_sigalrm_handler(int signum) {
if (is_json_log_on_timeout_enabled) {
vk::singleton::get().write_log_with_backtrace("Maximum execution time exceeded", E_ERROR);
}
- perform_error_if_running("timeout exit\n", script_error_t::timeout);
+ perform_error_if_running("timeout exit\n", script_error_t::timeout, signum);
}
}
}
@@ -88,13 +88,13 @@ void sigalrm_handler(int signum) {
setitimer(ITIMER_REAL, &timer, nullptr);
} else {
// if there's no shutdown functions terminate script now
- perform_error_if_running("soft timeout exit\n", script_error_t::timeout);
+ perform_error_if_running("soft timeout exit\n", script_error_t::timeout, signum);
}
} else {
kwrite_str(2, "hard timeout expired\n");
// [3] code in script context and this is the second timeout
// time to start shutdown functions has expired, emergency shutdown
- perform_error_if_running("hard timeout exit\n", script_error_t::timeout);
+ perform_error_if_running("hard timeout exit\n", script_error_t::timeout, signum);
}
}
}
@@ -104,7 +104,7 @@ void sigusr2_handler(int signum) {
if (check_signal_critical_section(signum, "SIGUSR2")) {
PhpScript::memory_limit_exceeded = true;
if (PhpScript::in_script_context) {
- perform_error_if_running("memory limit exit\n", script_error_t::memory_limit);
+ perform_error_if_running("memory limit exit\n", script_error_t::memory_limit, signum);
}
}
}
@@ -112,14 +112,14 @@ void sigusr2_handler(int signum) {
void php_assert_handler(int signum) {
kwrite_str(2, "in php_assert_handler (SIGRTMIN+1 signal)\n");
if (check_signal_critical_section(signum, "SIGRTMIN+1")) {
- perform_error_if_running("php assert error\n", script_error_t::php_assert);
+ perform_error_if_running("php assert error\n", script_error_t::php_assert,signum);
}
}
void stack_overflow_handler(int signum) {
kwrite_str(2, "in stack_overflow_handler (SIGRTMIN+2 signal)\n");
if (check_signal_critical_section(signum, "SIGRTMIN+2")) {
- perform_error_if_running("stack overflow error\n", script_error_t::stack_overflow);
+ perform_error_if_running("stack overflow error\n", script_error_t::stack_overflow, signum);
}
}
@@ -187,7 +187,7 @@ void sigsegv_handler(int signum, siginfo_t *info, void *ucontext) {
kwrite_str(2, "In critical section: calling _exit (124)\n");
_exit(124);
} else {
- PhpScript::error("sigsegv(stack overflow)", script_error_t::stack_overflow);
+ PhpScript::error("sigsegv(stack overflow)", script_error_t::stack_overflow, signum);
}
} else {
const char *msg = signum == SIGBUS ? "SIGBUS terminating program" : "SIGSEGV terminating program";
@@ -227,10 +227,10 @@ void sigabrt_handler(int, siginfo_t *info, void *) {
//mark no return
-void perform_error_if_running(const char *msg, script_error_t error_type) {
+void perform_error_if_running(const char *msg, script_error_t error_type, const std::optional &triggered_by_signal) {
if (PhpScript::in_script_context) {
kwrite_str(2, msg);
- PhpScript::error(msg, error_type);
+ PhpScript::error(msg, error_type, triggered_by_signal);
assert ("unreachable point" && 0);
}
}
diff --git a/server/signal-handlers.h b/server/signal-handlers.h
index 76596570d9..1587d953d9 100644
--- a/server/signal-handlers.h
+++ b/server/signal-handlers.h
@@ -5,7 +5,7 @@
#include "server/php-runner.h"
#include "server/workers-control.h"
-void perform_error_if_running(const char *msg, script_error_t error_type);
+void perform_error_if_running(const char *msg, script_error_t error_type, const std::optional &triggered_by_signal);
void init_handlers();
void worker_global_init_handlers(WorkerType worker_type);