diff --git a/src/kernel/emulation/linux/CMakeLists.txt b/src/kernel/emulation/linux/CMakeLists.txt index 70dc93430..ab66ec801 100644 --- a/src/kernel/emulation/linux/CMakeLists.txt +++ b/src/kernel/emulation/linux/CMakeLists.txt @@ -15,7 +15,11 @@ add_definitions(-DBSDTHREAD_WRAP_LINUX_PTHREAD include_directories(${CMAKE_SOURCE_DIR}/src/startup) -include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +# include src/startup for rtsig.h +include_directories(${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_BINARY_DIR}/src/startup +) set(emulation_sources elfcalls_wrapper.c @@ -132,6 +136,7 @@ set(emulation_sources signal/sigprocmask.c signal/sig_restorer.S signal/sigsuspend.c + signal/sigexc.c misc/ptrace.c misc/getentropy.c misc/syscall.c @@ -291,6 +296,7 @@ set_source_files_properties(misc/sysctl.c PROPERTIES COMPILE_FLAGS add_library(emulation OBJECT ${emulation_sources}) add_library(emulation_dyld OBJECT ${emulation_sources}) set_target_properties(emulation_dyld PROPERTIES COMPILE_FLAGS "-ffunction-sections -DVARIANT_DYLD") +add_dependencies(emulation rtsig_h) make_fat(emulation) make_fat(emulation_dyld) diff --git a/src/kernel/emulation/linux/mach/mach_traps.c b/src/kernel/emulation/linux/mach/mach_traps.c index a5468de83..5f80c87a2 100644 --- a/src/kernel/emulation/linux/mach/mach_traps.c +++ b/src/kernel/emulation/linux/mach/mach_traps.c @@ -518,6 +518,8 @@ kern_return_t syscall_thread_switch_impl( }; // Sleep for 1ms __linux_nanosleep(&tv, &tv); + + // TODO: we could implement this with yield_to() in LKM return KERN_SUCCESS; } diff --git a/src/kernel/emulation/linux/process/wait4.c b/src/kernel/emulation/linux/process/wait4.c index 89ba6d06d..b89cb8493 100644 --- a/src/kernel/emulation/linux/process/wait4.c +++ b/src/kernel/emulation/linux/process/wait4.c @@ -3,6 +3,8 @@ #include "../errno.h" #include "../signal/duct_signals.h" #include "../misc/ptrace.h" +#include "../mach/lkm.h" +#include "../../../../lkm/api.h" #include #include #include @@ -39,21 +41,18 @@ restart: signal = signum_linux_to_bsd(signal); *status = (*status & 0x7f) | (signal << 8); - switch (signal) + if (lkm_call(NR_get_tracer, (void*)(long) ret) == getpid()) { - case SIGCONT: - case SIGSTOP: - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - // It is standard behavior that these signals stop (resume) the process - break; - default: - // We are probably ptracing the target process. - // Allow the execution to continue so that the ptraced process can translate - // the signal into a Mach message. - sys_ptrace(PT_CONTINUE, ret, NULL, signal); - goto restart; + // We are ptracing the target process. + // Allow the execution to continue so that the ptraced process can translate + // the signal into a Mach message. + if (signal == SIGSTOP) + { + // TODO: notify target process it has been SIGSTOP'ed via sigqueue + } + + sys_ptrace(PT_CONTINUE, ret, NULL, signal); + goto restart; } } diff --git a/src/kernel/emulation/linux/signal/duct_signals.h b/src/kernel/emulation/linux/signal/duct_signals.h index a8efcecfd..fc07e8ffe 100644 --- a/src/kernel/emulation/linux/signal/duct_signals.h +++ b/src/kernel/emulation/linux/signal/duct_signals.h @@ -36,7 +36,6 @@ #define LINUX_SIGPWR 30 #define LINUX_SIGSYS 31 #define LINUX_SIGUNUSED 31 -#define LINUX_SIGRTMIN 32 #define LINUX_SA_NOCLDSTOP 0x00000001u #define LINUX_SA_NOCLDWAIT 0x00000002u #define LINUX_SA_SIGINFO 0x00000004u diff --git a/src/kernel/emulation/linux/signal/sigaction.c b/src/kernel/emulation/linux/signal/sigaction.c index 1dabc3182..1b667ff89 100644 --- a/src/kernel/emulation/linux/signal/sigaction.c +++ b/src/kernel/emulation/linux/signal/sigaction.c @@ -4,9 +4,9 @@ #include "../simple.h" #include #include +#include "sigexc.h" #include "../../../../../platform-include/sys/errno.h" -static void handler_linux_to_bsd(int linux_signum, struct linux_siginfo* info, void* ctxt); static int sigflags_bsd_to_linux(int flags); static int sigflags_linux_to_bsd(int flags); extern void sig_restorer(void); @@ -17,7 +17,9 @@ extern void* memset(void* dest, int v, __SIZE_TYPE__ len); // Libc uses only one trampoline void (*sa_tramp)(void*, int, int, struct bsd_siginfo*, void*) = 0; -static bsd_sig_handler* sig_handlers[32]; +bsd_sig_handler* sig_handlers[32]; +int sig_flags[32]; +unsigned int sig_masks[32]; long sys_sigaction(int signum, const struct bsd___sigaction* nsa, struct bsd_sigaction* osa) { @@ -42,7 +44,11 @@ long sys_sigaction(int signum, const struct bsd___sigaction* nsa, struct bsd_sig if (nsa != NULL) { sa_tramp = nsa->sa_tramp; - if (nsa->sa_sigaction != SIG_DFL && nsa->sa_sigaction != SIG_IGN + if (darling_am_i_ptraced()) + { + sa.sa_sigaction = &sigexc_handler; + } + else if (nsa->sa_sigaction != SIG_DFL && nsa->sa_sigaction != SIG_IGN && nsa->sa_sigaction != SIG_ERR) { sa.sa_sigaction = &handler_linux_to_bsd; @@ -71,10 +77,12 @@ long sys_sigaction(int signum, const struct bsd___sigaction* nsa, struct bsd_sig osa->sa_flags = sigflags_linux_to_bsd(olsa.sa_flags); } - if (nsa != NULL) + if (nsa != NULL && ret >= 0) { // __simple_printf("Saving handler for signal %d: %d\n", linux_signum, nsa->sa_sigaction); sig_handlers[linux_signum] = nsa->sa_sigaction; + sig_flags[linux_signum] = sa.sa_flags; + sig_masks[linux_signum] = sa.sa_mask; } return 0; @@ -120,7 +128,7 @@ static void ucontext_linux_to_bsd(const struct linux_ucontext* lc, struct bsd_uc #undef copyreg } -static void handler_linux_to_bsd(int linux_signum, struct linux_siginfo* info, void* ctxt) +void handler_linux_to_bsd(int linux_signum, struct linux_siginfo* info, void* ctxt) { int bsd_signum; struct bsd_siginfo binfo; diff --git a/src/kernel/emulation/linux/signal/sigaction.h b/src/kernel/emulation/linux/signal/sigaction.h index b55eef4f9..2041cd427 100644 --- a/src/kernel/emulation/linux/signal/sigaction.h +++ b/src/kernel/emulation/linux/signal/sigaction.h @@ -51,6 +51,7 @@ struct linux_siginfo union { int _pad[__SI_PAD_SIZE]; + unsigned long si_value; }; }; @@ -194,5 +195,10 @@ struct bsd_ucontext struct bsd_mcontext* uc_mcontext; }; +void handler_linux_to_bsd(int linux_signum, struct linux_siginfo* info, void* ctxt); +extern bsd_sig_handler* sig_handlers[32]; +extern int sig_flags[32]; +extern unsigned int sig_masks[32]; + #endif diff --git a/src/kernel/emulation/linux/signal/sigexc.c b/src/kernel/emulation/linux/signal/sigexc.c new file mode 100644 index 000000000..e05d64343 --- /dev/null +++ b/src/kernel/emulation/linux/signal/sigexc.c @@ -0,0 +1,113 @@ +#include "sigexc.h" +#include "../base.h" +#include +#include + +// Support for Darwin debugging. +// Unlike other Unix-like systems, macOS doesn't use wait() to handle events in the debugged process. + +static bool am_i_ptraced = false; +static void handle_rt_signal(int signum); +extern void sig_restorer(void); +void darling_sigexc_uninstall(void); +void sigrt_handler(int signum, struct linux_siginfo* info, void* ctxt); + +void sigexc_setup(void) +{ + // Setup handler for SIGNAL_SIGEXC_TOGGLE and SIGNAL_SIGEXC_THUPDATE + handle_rt_signal(SIGNAL_SIGEXC_TOGGLE); + handle_rt_signal(SIGNAL_SIGEXC_THUPDATE); +} + +static void handle_rt_signal(int signum) +{ + struct linux_sigaction sa; + sa.sa_sigaction = sigrt_handler; + sa.sa_mask = 0; + sa.sa_flags = LINUX_SA_RESTORER | LINUX_SA_SIGINFO | LINUX_SA_RESTART; + sa.sa_restorer = sig_restorer; + + LINUX_SYSCALL(__NR_rt_sigaction, signum, + &sa, NULL, + sizeof(sa.sa_mask)); +} + +bool darling_am_i_ptraced(void) +{ + return am_i_ptraced; +} + +void sigrt_handler(int signum, struct linux_siginfo* info, void* ctxt) +{ + if (signum == SIGNAL_SIGEXC_TOGGLE) + { + if (info->si_value == SIGRT_MAGIC_ENABLE_SIGEXC) + { + am_i_ptraced = true; + darling_sigexc_self(); + } + else if (info->si_value == SIGRT_MAGIC_DISABLE_SIGEXC) + { + am_i_ptraced = false; + darling_sigexc_uninstall(); + } + } + else if (signum == SIGNAL_SIGEXC_THUPDATE) + { + // TODO: Change how a pending signal is handled + // Use TLS? + if (!am_i_ptraced) + return; + } +} + +void darling_sigexc_self(void) +{ + // Make sigexc_handler the handler for all signals in the process + for (int i = 1; i <= 31; i++) + { + struct linux_sigaction sa; + sa.sa_sigaction = sigrt_handler; + sa.sa_mask = sig_masks[i]; + sa.sa_flags = LINUX_SA_RESTORER | LINUX_SA_SIGINFO | LINUX_SA_RESTART; + sa.sa_restorer = sig_restorer; + + LINUX_SYSCALL(__NR_rt_sigaction, i, + &sa, NULL, + sizeof(sa.sa_mask)); + } +} +void darling_sigexc_uninstall(void) +{ + for (int i = 1; i <= 31; i++) + { + struct linux_sigaction sa; + + if (sig_handlers[i] == SIG_DFL || sig_handlers[i] != SIG_IGN + || sig_handlers[i] != SIG_ERR) + { + sa.sa_sigaction = (linux_sig_handler*) sig_handlers[i]; + } + else + sa.sa_sigaction = &handler_linux_to_bsd; + + sa.sa_mask = sig_masks[i]; + sa.sa_flags = sig_flags[i]; + sa.sa_restorer = sig_restorer; + + LINUX_SYSCALL(__NR_rt_sigaction, i, + &sa, NULL, + sizeof(sa.sa_mask)); + } +} + +void sigexc_handler(int linux_signum, struct linux_siginfo* info, void* ctxt) +{ + // SIGSEGV + SIGBUS -> EXC_BAD_ACCESS + // SIGTRAP -> EXC_BREAKPOINT + // * -> EXC_SOFTWARE with EXC_SOFT_SIGNAL (e.g. SIGSTOP + if (darling_am_i_ptraced()) + { + } +} + diff --git a/src/kernel/emulation/linux/signal/sigexc.h b/src/kernel/emulation/linux/signal/sigexc.h new file mode 100644 index 000000000..eb7d661bf --- /dev/null +++ b/src/kernel/emulation/linux/signal/sigexc.h @@ -0,0 +1,25 @@ +#ifndef _SIGEXC_H +#define _SIGEXC_H +#include +#include "rtsig.h" +#include "sigaction.h" + +#define SIGNAL_SIGEXC_TOGGLE LINUX_SIGRTMIN +#define SIGNAL_SIGEXC_THUPDATE (LINUX_SIGRTMIN + 1) + +#define SIGRT_MAGIC_ENABLE_SIGEXC 0xdebdeb01 +#define SIGRT_MAGIC_DISABLE_SIGEXC 0xdebdeb00 +#define SIGRT_ + +// Initializes this module +void sigexc_setup(void); + +// Is this process currently traced by a debugger? +bool darling_am_i_ptraced(void); + +// for PT_SIGEXC to handle this operation synchronously +void darling_sigexc_self(void); +void sigexc_handler(int linux_signum, struct linux_siginfo* info, void* ctxt); + +#endif + diff --git a/src/lkm b/src/lkm index bb88121f9..622b9032e 160000 --- a/src/lkm +++ b/src/lkm @@ -1 +1 @@ -Subproject commit bb88121f91a6cdddd2dbc2eabc882d22e94e34c6 +Subproject commit 622b9032edea2fbb68af14eb4cfc6bf35c975180 diff --git a/src/startup/CMakeLists.txt b/src/startup/CMakeLists.txt index b9ef221ba..774a26322 100644 --- a/src/startup/CMakeLists.txt +++ b/src/startup/CMakeLists.txt @@ -59,3 +59,7 @@ if (DEFINED WITH_PRINT_WRAPPED_ELF) add_executable(print_wrapped_elf wrapgen/print_wrapped_elf.cpp) endif (DEFINED WITH_PRINT_WRAPPED_ELF) +add_executable(rtsig rtsig.c) +add_custom_command(OUTPUT rtsig.h DEPENDS rtsig COMMAND ${CMAKE_CURRENT_BINARY_DIR}/rtsig rtsig.h COMMENT "Determining available RT signals") +add_custom_target(rtsig_h DEPENDS rtsig.h) + diff --git a/src/startup/rtsig.c b/src/startup/rtsig.c new file mode 100644 index 000000000..7a69d5d39 --- /dev/null +++ b/src/startup/rtsig.c @@ -0,0 +1,25 @@ +#include +#include + +// Determine RT signal ranges without polluting the build environment with Linux system headers +int main(int argc, char** argv) +{ + FILE* output = stdout; + + if (argc > 1) + { + output = fopen(argv[1], "w"); + if (!output) + { + perror("fopen"); + return 1; + } + } + + fprintf(output, "#define LINUX_SIGRTMIN %d\n", SIGRTMIN); + fprintf(output, "#define LINUX_SIGRTMAX %d\n", SIGRTMAX); + + fclose(output); + return 0; +} +