Skip to content

Commit

Permalink
Add user context manipulation routines for linux x86_64
Browse files Browse the repository at this point in the history
Signed-off-by: Petr Shumilov <[email protected]>
  • Loading branch information
PetrShumilov committed Oct 27, 2024
1 parent 5e4b58a commit ab0b951
Show file tree
Hide file tree
Showing 25 changed files with 741 additions and 107 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
9 changes: 8 additions & 1 deletion common/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,14 @@ prepend(COMMON_TL_METHODS_SOURCES ${COMMON_DIR}/tl/methods/

if (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/
ucontext-arm.cpp)
darwin/aarch64/context.cpp)
else()
prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/
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)
endif()

set(COMMON_ALL_SOURCES
Expand Down
8 changes: 8 additions & 0 deletions common/dl-utils-lite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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_t *>(ucontext));
Expand Down
1 change: 1 addition & 0 deletions common/dl-utils-lite.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions common/server/crash-dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "common/kprintf.h"
#include "common/ucontext/ucontext-portable.h"
#include <ucontext.h>

struct crash_dump_buffer {
char scratchpad[1024];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstdlib>

enum { SP_OFFSET = 432, PC_OFFSET = 440, PSTATE_OFFSET = 448, FPSIMD_CONTEXT_OFFSET = 464 };
Expand All @@ -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 setcontext_portable(const libucontext_ucontext *);

__attribute__((visibility("hidden"))) void libucontext_trampoline() {
libucontext_ucontext *uc_link = nullptr;

Expand All @@ -40,10 +41,10 @@ __attribute__((visibility("hidden"))) void libucontext_trampoline() {
exit(0);
}

libucontext_setcontext(uc_link);
setcontext_portable(uc_link);
}

void libucontext_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc, ...) {
extern "C" void makecontext_portable(libucontext_ucontext *ucp, void (*func)(), int argc, ...) {
unsigned long *sp;
unsigned long *regp;
va_list va;
Expand Down Expand Up @@ -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(getcontext_portable) ";\n"
".align 2;\n"
NAME(libucontext_getcontext) ":\n"
NAME(getcontext_portable) ":\n"
"str xzr, [x0, #((184) + ((0) * (8)))]\n" // #REG_OFFSET(0)
/* save GPRs */
"stp x0, x1, [x0, #((184) + ((0) * (8)))]\n" // REG_OFFSET(0)
Expand Down Expand Up @@ -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(setcontext_portable) ";\n"
".align 2;\n"
NAME(libucontext_setcontext) ":\n"
NAME(setcontext_portable) ":\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)
Expand All @@ -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(swapcontext_portable) ";\n"
".align 2;\n"
NAME(libucontext_swapcontext) ":\n"
NAME(swapcontext_portable) ":\n"
"str xzr, [x0, #((184) + ((0) * (8)))]\n" // REG_OFFSET(0)
/* save GPRs */
"stp x2, x3, [x0, #((184) + ((2) * (8)))]\n" // REG_OFFSET(2)
Expand Down Expand Up @@ -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(setcontext_portable) "\n"
/* hmm, we came back here try to return */
"mov x30, x28\n"
"ret\n");
"ret\n");
54 changes: 54 additions & 0 deletions common/ucontext/darwin/aarch64/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Compiler for PHP (aka KPHP)
// Copyright (c) 2018-2022 Ariadne Conill <[email protected]>
// 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 <cstddef>

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<void *>(ctx.uc_mcontext.fp);
}
25 changes: 25 additions & 0 deletions common/ucontext/darwin/x86_64/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#include <ucontext.h>

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<void *>(ctx.uc_mcontext->__ss.__rbp);
}
26 changes: 26 additions & 0 deletions common/ucontext/linux/aarch64/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#include <ucontext.h>

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 -- Frame Pointer, based on AArch64 spec
return reinterpret_cast<void *>(ctx.uc_mcontext.regs[29]);
}
113 changes: 113 additions & 0 deletions common/ucontext/linux/x86_64/context.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include <cstddef>
#include <cstdint>
#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<void *>(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");
Loading

0 comments on commit ab0b951

Please sign in to comment.