Skip to content

Commit

Permalink
Removing the dependency on the libucontext library makes it easier to…
Browse files Browse the repository at this point in the history
… build for macOS on arm processors (#892)
  • Loading branch information
troy4eg authored Sep 21, 2023
1 parent 99d4257 commit daa5577
Show file tree
Hide file tree
Showing 9 changed files with 248 additions and 48 deletions.
8 changes: 7 additions & 1 deletion common/common.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,17 @@ prepend(COMMON_TL_METHODS_SOURCES ${COMMON_DIR}/tl/methods/
rwm.cpp
string.cpp)

if (APPLE AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/
ucontext.cpp)
endif()

set(COMMON_ALL_SOURCES
${COMMON_MAIN_SOURCES}
${COMMON_KFS_SOURCES}
${COMMON_TL_METHODS_SOURCES}
${COMMON_TL_SOURCES})
${COMMON_TL_SOURCES}
${COMMON_UCONTEXT_SOURCES})

if(COMPILER_CLANG)
set_source_files_properties(${COMMON_DIR}/string-processing.cpp PROPERTIES COMPILE_FLAGS -Wno-invalid-source-encoding)
Expand Down
2 changes: 1 addition & 1 deletion common/server/crash-dump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#include <unistd.h>

#include "common/kprintf.h"
#include "server/ucontext-portable.h"
#include "common/ucontext/ucontext-portable.h"

struct crash_dump_buffer {
char scratchpad[1024];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,9 @@

#else
// for M1, we can't use native makecontext() and others: they compile, but hang up when called
// instead, we require the ucontext library: https://github.com/kaniini/libucontext
// see the docs: https://vkcom.github.io/kphp/kphp-internals/developing-and-extending-kphp/compiling-kphp-from-sources.html
// here we expect, that libucontext include/ folder is copied into a default include path (to homebrew)
extern "C" {
#include <libucontext/libucontext.h>
}
#include "common/ucontext/ucontext.h"

#define ucontext_t_portable libucontext_ucontext_t
#define ucontext_t_portable libucontext_ucontext
#define setcontext_portable libucontext_setcontext
#define getcontext_portable libucontext_getcontext
#define makecontext_portable libucontext_makecontext
Expand Down
199 changes: 199 additions & 0 deletions common/ucontext/ucontext.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// Compiler for PHP (aka KPHP)
// libucontext (c) 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

#include "common/ucontext/ucontext.h"

#include <cstdarg>
#include <cstdint>
#include <cstdio>
#include <cstdlib>

enum { SP_OFFSET = 432, PC_OFFSET = 440, PSTATE_OFFSET = 448, FPSIMD_CONTEXT_OFFSET = 464 };

#define R0_OFFSET REG_OFFSET(0)

#define REG_OFFSET(__reg) (MCONTEXT_GREGS + ((__reg)*REG_SZ))

#define REG_SZ (8)
#define MCONTEXT_GREGS (184)

_Static_assert(offsetof(libucontext_ucontext, uc_mcontext.regs[0]) == R0_OFFSET, "R0_OFFSET is invalid");
_Static_assert(offsetof(libucontext_ucontext, uc_mcontext.sp) == SP_OFFSET, "SP_OFFSET is invalid");
_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");

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

asm("mov %0, x19" : "=r"((uc_link)));

if (uc_link == nullptr) {
exit(0);
}

libucontext_setcontext(uc_link);
}

void libucontext_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc, ...) {
unsigned long *sp;
unsigned long *regp;
va_list va;
int i;

sp = reinterpret_cast<unsigned long *>(reinterpret_cast<uintptr_t>(ucp->uc_stack.ss_sp) + ucp->uc_stack.ss_size);
sp -= argc < 8 ? 0 : argc - 8;
sp = reinterpret_cast<unsigned long *>(((reinterpret_cast<uintptr_t>(sp) & -16L)));

ucp->uc_mcontext.sp = reinterpret_cast<uintptr_t>(sp);
ucp->uc_mcontext.pc = reinterpret_cast<uintptr_t>(func);
ucp->uc_mcontext.regs[19] = reinterpret_cast<uintptr_t>(ucp->uc_link);
ucp->uc_mcontext.regs[30] = reinterpret_cast<uintptr_t>(&libucontext_trampoline);

va_start(va, argc);

regp = &(ucp->uc_mcontext.regs[0]);

for (i = 0; (i < argc && i < 8); i++) {
*regp++ = va_arg(va, unsigned long);
}

for (; i < argc; i++) {
*sp++ = va_arg(va, unsigned long);
}

va_end(va);
}

asm(R"(
.global _libucontext_getcontext;
.align 2;
_libucontext_getcontext:
str xzr, [x0, #((184) + ((0) * (8)))] // #REG_OFFSET(0)
/* save GPRs */
stp x0, x1, [x0, #((184) + ((0) * (8)))] // REG_OFFSET(0)
stp x2, x3, [x0, #((184) + ((2) * (8)))] // REG_OFFSET(2)
stp x4, x5, [x0, #((184) + ((4) * (8)))] // REG_OFFSET(4)
stp x6, x7, [x0, #((184) + ((6) * (8)))] // REG_OFFSET(6)
stp x8, x9, [x0, #((184) + ((8) * (8)))] // REG_OFFSET(8)
stp x10, x11, [x0, #((184) + ((10) * (8)))] // REG_OFFSET(10)
stp x12, x13, [x0, #((184) + ((12) * (8)))] // REG_OFFSET(12)
stp x14, x15, [x0, #((184) + ((14) * (8)))] // REG_OFFSET(14)
stp x16, x17, [x0, #((184) + ((16) * (8)))] // REG_OFFSET(16)
stp x18, x19, [x0, #((184) + ((18) * (8)))] // REG_OFFSET(18)
stp x20, x21, [x0, #((184) + ((20) * (8)))] // REG_OFFSET(20)
stp x22, x23, [x0, #((184) + ((22) * (8)))] // REG_OFFSET(22)
stp x24, x25, [x0, #((184) + ((24) * (8)))] // REG_OFFSET(24)
stp x26, x27, [x0, #((184) + ((26) * (8)))] // REG_OFFSET(26)
stp x28, x29, [x0, #((184) + ((28) * (8)))] // REG_OFFSET(28)
str x30, [x0, #((184) + ((30) * (8)))] // REG_OFFSET(30)
/* save current program counter in link register */
str x30, [x0, #440] // PC_OFFSET
/* save current stack pointer */
mov x2, sp
str x2, [x0, #432] // SP_OFFSET
/* save pstate */
str xzr, [x0, #448] // PSTATE_OFFSET
add x2, x0, #464 // FPSIMD_CONTEXT_OFFSET
stp q8, q9, [x2, #144]
stp q10, q11, [x2, #176]
stp q12, q13, [x2, #208]
stp q14, q15, [x2, #240]
mov x0, #0
ret
)");

asm(R"(
.global _libucontext_setcontext;
.align 2;
_libucontext_setcontext:
/* restore GPRs */
ldp x18, x19, [x0, #((184) + ((18) * (8)))] // REG_OFFSET(18)
ldp x20, x21, [x0, #((184) + ((20) * (8)))] // REG_OFFSET(20)
ldp x22, x23, [x0, #((184) + ((22) * (8)))] // REG_OFFSET(22)
ldp x24, x25, [x0, #((184) + ((24) * (8)))] // REG_OFFSET(24)
ldp x26, x27, [x0, #((184) + ((26) * (8)))] // REG_OFFSET(26)
ldp x28, x29, [x0, #((184) + ((28) * (8)))] // REG_OFFSET(28)
ldr x30, [x0, #((184) + ((30) * (8)))] // REG_OFFSET(30)
/* save current stack pointer */
ldr x2, [x0, #432] // SP_OFFSET
mov sp, x2
add x2, x0, #464 // FPSIMD_CONTEXT_OFFSET
ldp q8, q9, [x2, #144]
ldp q10, q11, [x2, #176]
ldp q12, q13, [x2, #208]
ldp q14, q15, [x2, #240]
/* save current program counter in link register */
ldr x16, [x0, #440] // PC_OFFSET
/* restore args */
ldp x2, x3, [x0, #((184) + ((2) * (8)))] // REG_OFFSET(2)
ldp x4, x5, [x0, #((184) + ((4) * (8)))] // REG_OFFSET(4)
ldp x6, x7, [x0, #((184) + ((6) * (8)))] // REG_OFFSET(6)
ldp x0, x1, [x0, #((184) + ((0) * (8)))] // REG_OFFSET(8)
/* jump to new PC */
br x16
)");

asm(R"(
.global _libucontext_swapcontext;
.align 2;
_libucontext_swapcontext:
str xzr, [x0, #((184) + ((0) * (8)))] // REG_OFFSET(0)
/* save GPRs */
stp x2, x3, [x0, #((184) + ((2) * (8)))] // REG_OFFSET(2)
stp x4, x5, [x0, #((184) + ((4) * (8)))] // REG_OFFSET(4)
stp x6, x7, [x0, #((184) + ((6) * (8)))] // REG_OFFSET(6)
stp x8, x9, [x0, #((184) + ((8) * (8)))] // REG_OFFSET(8)
stp x10, x11, [x0, #((184) + ((10) * (8)))] // REG_OFFSET(10)
stp x12, x13, [x0, #((184) + ((12) * (8)))] // REG_OFFSET(12)
stp x14, x15, [x0, #((184) + ((14) * (8)))] // REG_OFFSET(14)
stp x16, x17, [x0, #((184) + ((16) * (8)))] // REG_OFFSET(16)
stp x18, x19, [x0, #((184) + ((18) * (8)))] // REG_OFFSET(18)
stp x20, x21, [x0, #((184) + ((20) * (8)))] // REG_OFFSET(20)
stp x22, x23, [x0, #((184) + ((22) * (8)))] // REG_OFFSET(22)
stp x24, x25, [x0, #((184) + ((24) * (8)))] // REG_OFFSET(24)
stp x26, x27, [x0, #((184) + ((26) * (8)))] // REG_OFFSET(26)
stp x28, x29, [x0, #((184) + ((28) * (8)))] // REG_OFFSET(28)
str x30, [x0, #((184) + ((30) * (8)))] // REG_OFFSET(30)
/* save current program counter in link register */
str x30, [x0, #440] // PC_OFFSET
/* save current stack pointer */
mov x2, sp
str x2, [x0, #432] // SP_OFFSET
/* save pstate */
str xzr, [x0, #448] // PSTATE_OFFSET
add x2, x0, #464 // FPSIMD_CONTEXT_OFFSET
stp q8, q9, [x2, #144]
stp q10, q11, [x2, #176]
stp q12, q13, [x2, #208]
stp q14, q15, [x2, #240]
/* context to swap to is in x1 so... we move to x0 and call setcontext */
/* store our link register in x28 */
mov x28, x30
/* move x1 to x0 and call setcontext */
mov x0, x1
bl _libucontext_setcontext
/* hmm, we came back here try to return */
mov x30, x28
ret
)");
37 changes: 37 additions & 0 deletions common/ucontext/ucontext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// 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[31];
unsigned long 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 *);
}
6 changes: 0 additions & 6 deletions compiler/compiler-settings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -332,12 +332,6 @@ void CompilerSettings::init() {
ld_flags.value_ += " -L /usr/local/lib";
#endif

#if defined(__APPLE__) && defined(__arm64__)
// for development under M1, manual installation of libucontext is needed
// see the docs: https://vkcom.github.io/kphp/kphp-internals/developing-and-extending-kphp/compiling-kphp-from-sources.html
ld_flags.value_ += " /opt/homebrew/lib/libucontext.a";
#endif

std::vector<vk::string_view> external_libs{"pthread", "m", "dl"};

#ifdef PDO_DRIVER_MYSQL
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,34 +107,6 @@ cmake ..
sudo make install
```

##### MacOS with Apple M1 chipset

```note
Probably, there is an easier way to do this, but I couldn't find it, at least for late 2021.
```

Follow the steps above. Later on, you'll have to patch the [libucontext](https://github.com/kaniini/libucontext) library locally.
```bash
git clone https://github.com/kaniini/libucontext
cd libucontext
nano Makefile
```
Find assignment to `LIBUCONTEXT_LINKER_FLAGS` and replace it with:
```text
LIBUCONTEXT_LINKER_FLAGS = -dynamiclib -install_name ${LIBUCONTEXT_SONAME} -current_version ${LIBUCONTEXT_SOVERSION} -compatibility_version ${LIBUCONTEXT_SOVERSION}
```
Now we are ready to make libucontext:
```
make FREESTANDING=yes ARCH=aarch64
```
Finally, copy resulting files to the default brew search folder `/opt/homebrew`:
```
cp libucontext.a /opt/homebrew/lib
cp libucontext.dylib /opt/homebrew/lib
cp -r include/* /opt/homebrew/include/
```


##### Other Linux
Make sure you are using the same package list. You may use system default libcurl package, it would work, but without DNS resolving. `uber-h3` must be installed from sources.

Expand Down
4 changes: 2 additions & 2 deletions server/php-runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
#include <csetjmp>

#include "common/dl-utils-lite.h"
#include "common/kprintf.h"
#include "common/mixin/not_copyable.h"
#include "common/sanitizer.h"
#include "common/kprintf.h"
#include "common/ucontext/ucontext-portable.h"

#include "server/php-engine-vars.h"
#include "server/php-init-scripts.h"
#include "server/php-queries-types.h"
#include "server/php-query-data.h"
#include "server/ucontext-portable.h"

DECLARE_VERBOSITY(php_runner);

Expand Down
3 changes: 0 additions & 3 deletions tests/tests.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ if(KPHP_TESTS)
set(TEST_NAME unittests-${TEST_NAME})
add_executable(${TEST_NAME} ${ARGN})
target_link_libraries(${TEST_NAME} PRIVATE GTest::GTest GTest::Main gmock ${SRC_LIBS} vk::popular_common)
if(NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND APPLE)
target_link_libraries(${TEST_NAME} PRIVATE /opt/homebrew/lib/libucontext.a)
endif()
target_link_options(${TEST_NAME} PRIVATE ${NO_PIE})

# because of https://github.com/VKCOM/kphp/actions/runs/5463884925/jobs/9945150190
Expand Down

0 comments on commit daa5577

Please sign in to comment.