From nobody Mon Sep 17 00:00:00 2001 From: Ilya Berezhniuk Date: Mon, 25 Feb 2008 04:06:05 +0300 Subject: [PATCH] Implemented universal transfer control stubs in PORT; a part of hythread was moved to PORT The stubs allow to do the following: 1) transfer control to register context specified in Registers structure 2) setup Registers structure for calling C function with (max. 6) arguments and then return to the interrupted register context (probably modified in this function) 3) perform a correct call to C function with arguments and return from this function to (probably modified) register context Signals/exceptions processing was adopted for using these stubs. Using these stubs really simplifies the signals/exceptions handling code and eliminates platform-dependent preparation of arguments for C functions. This patch also does the following changes: - moves debugging functions from hythread to PORT (thread suspend/resume, set/get context..) - moves the functions for converting OS context to Registers and vv from VM core to PORT --- make/vm/port.xml | 11 vm/include/open/hythread_ext.h | 9 vm/include/open/ncai_thread.h | 15 - vm/port/include/port_thread.h | 85 +++ vm/port/src/thread/linux/thread_asm_em64t.s | 43 +- vm/port/src/thread/linux/thread_asm_ia32.s | 43 +- vm/port/src/thread/linux/thread_em64t.c | 144 +++++- vm/port/src/thread/linux/thread_ia32.c | 99 ++++ vm/port/src/thread/linux/thread_ipf.c | 49 ++ vm/port/src/thread/linux/thread_os.c | 522 ++++++++++++++++++++ vm/port/src/thread/win/thread_asm_em64t.asm | 49 ++ vm/port/src/thread/win/thread_asm_ia32.asm | 45 ++ vm/port/src/thread/win/thread_em64t.c | 137 ++++- vm/port/src/thread/win/thread_ia32.c | 134 +++++ vm/port/src/thread/win/thread_os.c | 371 ++++++++++++++ vm/thread/src/linux/os_thread.c | 510 -------------------- vm/thread/src/thread_native_basic.c | 5 vm/thread/src/thread_native_thin_monitor.c | 5 vm/thread/src/thread_ncai_common.c | 15 - vm/thread/src/thread_private.h | 7 vm/thread/src/win/os_thread.c | 349 ------------- vm/vmcore/include/exceptions_jit.h | 4 vm/vmcore/include/jvmti_break_intf.h | 6 vm/vmcore/include/vm_threads.h | 2 vm/vmcore/src/jvmti/jvmti_break_intf.cpp | 68 +-- vm/vmcore/src/ncai/utils/ncai_utils_em64t.cpp | 6 vm/vmcore/src/ncai/utils/ncai_utils_ia32.cpp | 6 vm/vmcore/src/ncai/utils/ncai_utils_ipf.cpp | 6 vm/vmcore/src/ncai/utils/ncai_utils_linux.cpp | 6 vm/vmcore/src/ncai/utils/ncai_utils_win.cpp | 6 vm/vmcore/src/thread/thread_manager.cpp | 4 vm/vmcore/src/util/linux/crash_handler.cpp | 1 .../src/util/linux/ia32_em64t/signals_common.cpp | 134 ++---- .../src/util/linux/include/platform_lowlevel.h | 19 - vm/vmcore/src/util/linux/include/signals_common.h | 43 -- vm/vmcore/src/util/linux/os_wrapper.cpp | 6 vm/vmcore/src/util/linux/signals_ipf.cpp | 33 - vm/vmcore/src/util/linux/stack_dump_platf.cpp | 1 vm/vmcore/src/util/native_stack.cpp | 20 + .../src/util/win/em64t/exception_handlers.asm | 56 -- .../src/util/win/ia32/nt_exception_filter.cpp | 66 --- .../win/ia32_em64t/nt_exception_filter_common.cpp | 44 +- vm/vmcore/src/util/win/include/exception_filter.h | 18 - 43 files changed, 1803 insertions(+), 1399 deletions(-) create mode 100755 vm/port/src/thread/linux/thread_os.c create mode 100755 vm/port/src/thread/win/thread_ia32.c create mode 100755 vm/port/src/thread/win/thread_os.c mode change 100644 => 100755 vm/vmcore/src/util/linux/ia32_em64t/signals_common.cpp eb8a1efe2c146bab2f696447134ee2c74dd7512b diff --git a/make/vm/port.xml b/make/vm/port.xml index 7ed5d58..3664591 100644 --- a/make/vm/port.xml +++ b/make/vm/port.xml @@ -38,7 +38,6 @@ - @@ -48,13 +47,19 @@ - - + + + + + + + + diff --git a/vm/include/open/hythread_ext.h b/vm/include/open/hythread_ext.h index 47a46e3..176896c 100644 --- a/vm/include/open/hythread_ext.h +++ b/vm/include/open/hythread_ext.h @@ -153,10 +153,17 @@ #define hycond_t struct HyCond #include "hycond_win.h" #endif // _WIN32 -#if defined(__linux__) || defined(FREEBSD) +#if defined(__linux__) || defined(FREEBSD) + +#include #define osthread_t pthread_t +#define thread_context_t ucontext_t + #elif _WIN32 + #define osthread_t HANDLE +#define thread_context_t CONTEXT + #else // !_WIN32 && !__linux__ #error "threading is only supported on __linux__ or _WIN32" #endif // !_WIN32 && !__linux__ diff --git a/vm/include/open/ncai_thread.h b/vm/include/open/ncai_thread.h index 0910019..646726b 100644 --- a/vm/include/open/ncai_thread.h +++ b/vm/include/open/ncai_thread.h @@ -33,16 +33,7 @@ #define OPEN_NCAI_THREAD_H #include "jvmti_types.h" #include "hythread_ext.h" - - -#ifdef WIN32 -/* Thread context definition for Windows */ -typedef CONTEXT os_thread_context_t; -#else -/* Thread context definition for UNIX-like systems */ -#include -typedef ucontext_t os_thread_context_t; -#endif +#include "port_thread.h" #ifdef __cplusplus @@ -62,9 +53,9 @@ extern HY_CFUNC int VMCALL hythread_get_suspend_count_native PROTOTYPE((hythread_t thread)); extern HY_CFUNC IDATA VMCALL -hythread_get_thread_context PROTOTYPE((hythread_t thread, os_thread_context_t* pcontext)); +hythread_get_thread_context PROTOTYPE((hythread_t thread, thread_context_t* pcontext)); extern HY_CFUNC IDATA VMCALL -hythread_set_thread_context PROTOTYPE((hythread_t thread, os_thread_context_t* pcontext)); +hythread_set_thread_context PROTOTYPE((hythread_t thread, thread_context_t* pcontext)); //@} diff --git a/vm/port/include/port_thread.h b/vm/port/include/port_thread.h index b1551b8..78904bb 100755 --- a/vm/port/include/port_thread.h +++ b/vm/port/include/port_thread.h @@ -23,8 +23,32 @@ #define _PORT_THREAD_H_ * @brief PORT thread support */ +/* For osthread_t and thread_context_t types */ +#include "open/hythread_ext.h" +/* Thread context definition for UNIX-like systems */ +#if defined(LINUX) || defined(FREEBSD) +#if defined(LINUX) + +#include +#include +#include + +#ifdef _syscall0 +static _syscall0(pid_t, gettid)/* static definition */ +#else /* _syscall0 */ +#include +#include +#define gettid() ((pid_t)syscall(__NR_gettid)) +#endif /* _syscall0 */ + +#else /* !LINUX */ +#define gettid() getpid() +#endif + +#endif /* LINUX || FREEBSD */ + /* To skip platform_types.h inclusion */ typedef struct Registers Registers; @@ -33,13 +57,70 @@ extern "C" { #endif /* __cplusplus */ -/** @name Threads manipulation and information +/** @name OS thread operations */ //@{ +void port_thread_yield_other(osthread_t thread); +int port_thread_cancel(osthread_t os_thread); + +int port_thread_suspend(osthread_t thread); +int port_thread_resume(osthread_t thread); +int port_thread_get_suspend_count(osthread_t thread); + +int port_thread_get_context(osthread_t thread, thread_context_t* pcontext); +int port_thread_set_context(osthread_t thread, thread_context_t* pcontext); + +void port_thread_context_to_regs(Registers* regs, thread_context_t* context); +void port_thread_regs_to_context(thread_context_t* context, Registers* regs); + /* Transfer control to specified register context */ -void transfer_to_regs(Registers* regs); +void port_transfer_to_regs(Registers* regs); + +/** +* Prepares 'Registers' structure and stack area pointed in for calling +* 'fn' function with a set of arguments provided in variable args list. +* THe 'fn' function is called through a special stub function with +* preserving 'red zone' on Linux and clearing direction flag on Windows. +* After returning from 'fn' and stub, processor registers are restored +* with a values provided in 'regs' argument. +* The function can be used to prepare register context for transfering +* a control to a signal/exception handling function out of the OS handler. +* +* When the first argument passed to 'fn' is the same 'regs' pointer, its +* value is substituted with the pointer stored 'Registers' structure used +* to restore register context. If 'fn' function modifies the context +* pointed by the first argument, these changes will take effect after +* returning from 'fn'. +* +* The stub for calling 'fn' is written in assembler language; 'Registers' +* fields and size are hardcoded. It would be better to rewrite it using +* encoder in future, to keep control on 'Registers' structure and size. +* +* @param [in] fn - the address of the function to be called +* @param [in] regs - the register context +* @param [in] num - the number of parameters passed to the 'fn' function +* in the variable args list (6 args at maximum) +* @param [in] ... - the parameters for 'fn'; should all be void* or of +* the same size (pointer-sized) +*/ +void port_set_longjump_regs(void* fn, Registers* regs, int num, ...); + +/** +* The same as 'port_set_longjump_regs', but transfers a control to the +* prepared registers context by itself. +* Actually it's a combination of 'port_set_longjump_regs' and +* 'port_transfer_to_regs' functions, but 'regs' fields are kept unchanged. +* +* @param [in] fn - the address of the function to be called +* @param [in] regs - the register context +* @param [in] num - the number of parameters passed to the 'fn' function +* in the variable args list (6 args at maximum) +* @param [in] ... - the parameters for 'fn'; should all be void* or of +* the same size (pointer-sized) +*/ +void port_transfer_to_function(void* fn, Registers* regs, int num, ...); diff --git a/vm/port/src/thread/linux/thread_asm_em64t.s b/vm/port/src/thread/linux/thread_asm_em64t.s index 1d88ab6..72d82c6 100755 --- a/vm/port/src/thread/linux/thread_asm_em64t.s +++ b/vm/port/src/thread/linux/thread_asm_em64t.s @@ -41,11 +41,11 @@ // uint32 eflags;; 88h // }; // -// void transfer_to_regs(Registers* regs) +// void port_transfer_to_regs(Registers* regs) -.globl transfer_to_regs - .type transfer_to_regs, @function -transfer_to_regs: +.globl port_transfer_to_regs + .type port_transfer_to_regs, @function +port_transfer_to_regs: movq %rdi, %rdx // regs pointer (1st param - RDI) -> RDX movq 0x08(%rdx), %rbp // RBP field @@ -80,3 +80,38 @@ __skipefl__: movq (%rsp), %rsp // load new RSP jmpq * -0x88(%rsp) // JMP to new RIP + +// void port_longjump_stub(void) +// +// after returning from the called function, RSP points to the 2 argument +// slots in the stack. Saved Registers structure pointer is (RSP + 48) +// +// | interrupted | +// | program | <- RSP where the program was interrupted by signal +// |-------------| +// | 0x80 bytes | <- 'red zone' - we will not change it +// |-------------| +// | return addr | +// | from stub | <- for using in port_transfer_to_regs as [(new RSP) - 128 - 8] +// |-------------| +// | saved | +// | Registers | <- to restore register context +// |-------------| +// | [alignment] | <- align Regs pointer to 16-bytes boundary +// |-------------| +// | pointer to | +// | saved Regs | <- (RSP + 128) +// // |-------------| +// // | 0x80 bytes | <- 'red zone' +// |-------------| +// | return addr | +// | from 'fn' | <- address to return to the port_longjump_stub +// |-------------| + +.globl port_longjump_stub + .type port_longjump_stub, @function +port_longjump_stub: +// movq 128(%rsp), %rcx // load RCX with the address of saved Registers + movq (%rsp), %rcx // load RCX with the address of saved Registers + callq port_transfer_to_regs // restore context + ret // dummy RET - unreachable diff --git a/vm/port/src/thread/linux/thread_asm_ia32.s b/vm/port/src/thread/linux/thread_asm_ia32.s index 0530184..ceae264 100755 --- a/vm/port/src/thread/linux/thread_asm_ia32.s +++ b/vm/port/src/thread/linux/thread_asm_ia32.s @@ -30,11 +30,11 @@ // uint32 eflags; +24 // }; // -// void transfer_to_regs(Registers* regs) +// void port_transfer_to_regs(Registers* regs) -.globl transfer_to_regs - .type transfer_to_regs, @function -transfer_to_regs: +.globl port_transfer_to_regs + .type port_transfer_to_regs, @function +port_transfer_to_regs: movl 0x04(%esp), %edx // store regs pointer to EDX movl 0x20(%edx), %ebx // EIP field -> EBX movl 0x1C(%edx), %ecx // ESP field @@ -56,3 +56,38 @@ _label_: movl 0x0C(%edx), %edx // EDX field movl 0x04(%esp), %esp // ((new ESP - 4) -> ESP ret // JMP by RET + + +// void port_longjump_stub(void) +// +// after returning from the called function, EBP points to the pointer +// to saved Registers structure +// +// | interrupted | +// | program | <- ESP where the program was interrupted by signal +// |-------------| +// | return addr | +// | from stub | <- for using in port_transfer_to_regs +// |-------------| +// | saved | +// | Registers | <- to restore register context +// |-------------| +// | pointer to | +// | saved Regs | <- EBP +// |-------------| +// | arg 5 | <- +// |-------------| | +// ............... - arguments for 'fn' +// |-------------| | +// | arg 0 | <- +// |-------------| +// | return addr | +// | from 'fn' | <- address to return to the port_longjump_stub +// |-------------| + +.globl port_longjump_stub + .type port_longjump_stub, @function +port_longjump_stub: + movl %ebp, %esp // ESP now points to the address of saved Registers + call port_transfer_to_regs // restore context + ret // dummy RET - unreachable diff --git a/vm/port/src/thread/linux/thread_em64t.c b/vm/port/src/thread/linux/thread_em64t.c index fcd3bd0..a07057d 100644 --- a/vm/port/src/thread/linux/thread_em64t.c +++ b/vm/port/src/thread/linux/thread_em64t.c @@ -16,13 +16,12 @@ */ +#define _GNU_SOURCE #include -#include -#include "vm_core_types.h" -#include "signals_common.h" +#include "port_thread.h" -void ucontext_to_regs(Registers* regs, ucontext_t *uc) +void port_thread_context_to_regs(Registers* regs, ucontext_t *uc) { regs->rax = uc->uc_mcontext.gregs[REG_RAX]; regs->rcx = uc->uc_mcontext.gregs[REG_RCX]; @@ -44,7 +43,7 @@ void ucontext_to_regs(Registers* regs, u regs->eflags = uc->uc_mcontext.gregs[REG_EFL]; } -void regs_to_ucontext(ucontext_t *uc, Registers* regs) +void port_thread_regs_to_context(ucontext_t *uc, Registers* regs) { uc->uc_mcontext.gregs[REG_RAX] = regs->rax; uc->uc_mcontext.gregs[REG_RCX] = regs->rcx; @@ -66,28 +65,123 @@ void regs_to_ucontext(ucontext_t *uc, Re uc->uc_mcontext.gregs[REG_EFL] = regs->eflags; } -// Max. 6 arguments can be set up -void regs_push_param(Registers* pregs, POINTER_SIZE_INT param, int num) + +void port_longjump_stub(void); +#define DIR_FLAG ((uint32)0x00000400) + +void port_set_longjump_regs(void* fn, Registers* regs, int num, ...) { - switch (num) - { - case 0: - pregs->rdi = param; - return; - case 1: - pregs->rsi = param; - return; - case 2: - pregs->rdx = param; - return; - case 3: - pregs->rcx = param; - return; - case 4: - pregs->r8 = param; + void** sp; + va_list ap; + int i; + size_t align; + void** p_pregs; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + + if (!regs) return; - case 5: - pregs->r9 = param; + + sp = (void**)regs->rsp - 16 - 1; /* preserve 128-bytes 'red zone' */ + *sp = (void*)regs->rip; + align = !((rcount & 1) ^ (((uint64)sp & sizeof(void*)) != 0)); + p_pregs = sp - rcount - align - 1; + sp = sp - rcount; + *((Registers*)sp) = *regs; + *p_pregs = (void*)sp; + +// sp = p_pregs - 16 - 1; /* preserve 128-bytes 'red zone' */ + sp = p_pregs - 1; /* set sp to return address */ + + va_start(ap, num); + + if (num > 0) + { + void* arg = va_arg(ap, void*); + if (arg == regs) + regs->rdi = (uint64)(*p_pregs); /* Replace 1st arg */ + else + regs->rdi = (uint64)arg; + } + + if (num > 1) + regs->rsi = (uint64)va_arg(ap, void*); + + if (num > 2) + regs->rdx = (uint64)va_arg(ap, void*); + + if (num > 3) + regs->rcx = (uint64)va_arg(ap, void*); + + if (num > 4) + regs->r8 = (uint64)va_arg(ap, void*); + + if (num > 5) + regs->r9 = (uint64)va_arg(ap, void*); + + *sp = (void*)&port_longjump_stub; + regs->rsp = (uint64)sp; + regs->rip = (uint64)fn; + regs->eflags = regs->eflags & ~DIR_FLAG; +} + +void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) +{ + void** sp; + va_list ap; + int i; + size_t align; + void** p_pregs; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + Registers regs; + + if (!pregs) return; + + regs = *pregs; + + sp = (void**)regs.rsp - 16 - 1; /* preserve 128-bytes 'red zone' */ + *sp = (void*)regs.rip; + align = !((rcount & 1) ^ (((uint64)sp & sizeof(void*)) != 0)); + p_pregs = sp - rcount - align - 1; + sp = sp - rcount; + *((Registers*)sp) = regs; + *p_pregs = (void*)sp; + +// sp = p_pregs - 16 - 1; /* preserve 128-bytes 'red zone' */ + sp = p_pregs - 1; /* set sp to return address */ + + va_start(ap, num); + + if (num > 0) + { + void* arg = va_arg(ap, void*); + if (arg == pregs) + regs.rdi = (uint64)(*p_pregs); /* Replace 1st arg */ + else + regs.rdi = (uint64)arg; } + + if (num > 1) + regs.rsi = (uint64)va_arg(ap, void*); + + if (num > 2) + regs.rdx = (uint64)va_arg(ap, void*); + + if (num > 3) + regs.rcx = (uint64)va_arg(ap, void*); + + if (num > 4) + regs.r8 = (uint64)va_arg(ap, void*); + + if (num > 5) + regs.r9 = (uint64)va_arg(ap, void*); + + *sp = (void*)&port_longjump_stub; + regs.rsp = (uint64)sp; + regs.rip = (uint64)fn; + regs.eflags = regs.eflags & ~DIR_FLAG; + + port_transfer_to_regs(®s); } diff --git a/vm/port/src/thread/linux/thread_ia32.c b/vm/port/src/thread/linux/thread_ia32.c index 5c52f7c..45ad9bc 100644 --- a/vm/port/src/thread/linux/thread_ia32.c +++ b/vm/port/src/thread/linux/thread_ia32.c @@ -16,14 +16,14 @@ */ +#define _GNU_SOURCE #include -#include -#include "vm_core_types.h" -#include "signals_common.h" +#include +#include "port_thread.h" #if defined(LINUX) -void ucontext_to_regs(Registers* regs, ucontext_t *uc) +void port_thread_context_to_regs(Registers* regs, ucontext_t *uc) { regs->eax = uc->uc_mcontext.gregs[REG_EAX]; regs->ecx = uc->uc_mcontext.gregs[REG_ECX]; @@ -37,7 +37,7 @@ void ucontext_to_regs(Registers* regs, u regs->eflags = uc->uc_mcontext.gregs[REG_EFL]; } -void regs_to_ucontext(ucontext_t *uc, Registers* regs) +void port_thread_regs_to_context(ucontext_t *uc, Registers* regs) { uc->uc_mcontext.gregs[REG_EAX] = regs->eax; uc->uc_mcontext.gregs[REG_ECX] = regs->ecx; @@ -52,7 +52,7 @@ void regs_to_ucontext(ucontext_t *uc, Re } #elif defined(FREEBSD) -void ucontext_to_regs(Registers* regs, ucontext_t *uc) +void port_thread_context_to_regs(Registers* regs, ucontext_t *uc) { regs->eax = uc->uc_mcontext.mc_eax; regs->ecx = uc->uc_mcontext.mc_ecx; @@ -66,7 +66,7 @@ void ucontext_to_regs(Registers* regs, u regs->eflags = uc->uc_mcontext.mc_eflags; } -void regs_to_ucontext(ucontext_t *uc, Registers* regs) +void port_thread_regs_to_context(ucontext_t *uc, Registers* regs) { uc->uc_mcontext.mc_eax = regs->eax; uc->uc_mcontext.mc_ecx = regs->ecx; @@ -83,3 +83,88 @@ void regs_to_ucontext(ucontext_t *uc, Re #else #error need to add correct mcontext_t lookup for registers #endif + + +void port_longjump_stub(void); +#define DIR_FLAG ((uint32)0x00000400) + +void port_set_longjump_regs(void* fn, Registers* regs, int num, ...) +{ + void** sp; + va_list ap; + int i; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + + if (!regs) + return; + + sp = (void**)regs->esp - 1; + *sp = (void*)regs->eip; + sp = sp - rcount - 1; + *((Registers*)(sp + 1)) = *regs; + *sp = (void*)(sp + 1); + regs->ebp = (uint32)sp; + + sp = sp - num - 1; + + va_start(ap, num); + + for (i = 1; i <= num; i = i + 1) + { + void* arg = va_arg(ap, void*); + + if (i == 1 && arg == regs) + sp[i] = *((void**)regs->ebp); /* Replace 1st arg */ + else + sp[i] = arg; + } + + *sp = (void*)&port_longjump_stub; + regs->esp = (uint32)sp; + regs->eip = (uint32)fn; + regs->eflags = regs->eflags & ~DIR_FLAG; +} + +void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) +{ + void** sp; + va_list ap; + int i; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + Registers regs; + + if (!pregs) + return; + + regs = *pregs; + + sp = (void**)regs.esp - 1; + *sp = (void*)regs.eip; + sp = sp - rcount - 1; + *((Registers*)(sp + 1)) = regs; + *sp = (void*)(sp + 1); + regs.ebp = (uint32)sp; + + sp = sp - num - 1; + + va_start(ap, num); + + for (i = 1; i <= num; i = i + 1) + { + void* arg = va_arg(ap, void*); + + if (i == 1 && arg == pregs) + sp[i] = *((void**)regs.ebp); /* Replace 1st arg */ + else + sp[i] = arg; + } + + *sp = (void*)&port_longjump_stub; + regs.esp = (uint32)sp; + regs.eip = (uint32)fn; + regs.eflags = regs.eflags & ~DIR_FLAG; + + port_transfer_to_regs(®s); +} diff --git a/vm/port/src/thread/linux/thread_ipf.c b/vm/port/src/thread/linux/thread_ipf.c index 7b4b70a..6497900 100755 --- a/vm/port/src/thread/linux/thread_ipf.c +++ b/vm/port/src/thread/linux/thread_ipf.c @@ -18,13 +18,56 @@ #include #include #include -#include "open/platform_types.h" +#include +#include "port_thread.h" -void transfer_to_regs(Registers* regs) +void port_transfer_to_regs(Registers* regs) { // FIXME: not implemented - fprintf(stderr, "FIXME: transfer_to_regs: not implemented\n"); + fprintf(stderr, "FIXME: port_transfer_to_regs: not implemented\n"); + assert(0); + abort(); +} + +void port_thread_context_to_regs(Registers* regs, ucontext_t* uc) +{ + memcpy(regs->gr, uc->uc_mcontext.sc_gr, sizeof(regs->gr)); + memcpy(regs->fp, uc->uc_mcontext.sc_fr, sizeof(regs->fp)); + memcpy(regs->br, uc->uc_mcontext.sc_br, sizeof(regs->br)); + + regs->preds = uc->uc_mcontext.sc_pr; + regs->nats = uc->uc_mcontext.sc_ar_rnat; + regs->pfs = uc->uc_mcontext.sc_ar_pfs; + regs->bsp = (uint64*)uc->uc_mcontext.sc_ar_bsp; + regs->ip = uc->uc_mcontext.sc_ip; +} + +void port_thread_regs_to_context(ucontext_t* uc, Registers* regs) +{ + memcpy(uc->uc_mcontext.sc_gr, regs->gr, sizeof(regs->gr)); + memcpy(uc->uc_mcontext.sc_fr, regs->fp, sizeof(regs->fp)); + memcpy(uc->uc_mcontext.sc_br, regs->br, sizeof(regs->br)); + + uc->uc_mcontext.sc_pr = regs->preds; + uc->uc_mcontext.sc_ar_rnat = regs->nats; + uc->uc_mcontext.sc_ar_pfs = regs->pfs; + uc->uc_mcontext.sc_ar_bsp = (uint64)regs->bsp; + uc->uc_mcontext.sc_ip = regs->ip; +} + +void port_set_longjump_regs(void* fn, Registers* regs, int num, ...) +{ + // FIXME: not implemented + fprintf(stderr, "FIXME: port_set_longjump_regs: not implemented\n"); + assert(0); + abort(); +} + +void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) +{ + // FIXME: not implemented + fprintf(stderr, "FIXME: port_transfer_to_function: not implemented\n"); assert(0); abort(); } diff --git a/vm/port/src/thread/linux/thread_os.c b/vm/port/src/thread/linux/thread_os.c new file mode 100755 index 0000000..8fbec48 --- /dev/null +++ b/vm/port/src/thread/linux/thread_os.c @@ -0,0 +1,522 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define _GNU_SOURCE +#include +#include // sched_param +#include +#include +#include +#include +#include +#include "port_thread.h" + + + +typedef enum +{ + THREADREQ_NONE = 0, + THREADREQ_SUS = 1, + THREADREQ_RES = 2, + THREADREQ_YIELD = 3 +} os_suspend_req_t; + +typedef struct os_thread_info_t os_thread_info_t; + +struct os_thread_info_t +{ + osthread_t thread; + int suspend_count; + sem_t wake_sem; /* to sem_post from signal handler */ + thread_context_t context; + + os_thread_info_t* next; +}; + + +/* Global mutex to syncronize access to os_thread_info_t list */ +static pthread_mutex_t g_suspend_mutex; +/* Global list with suspended threads info */ +static os_thread_info_t* g_suspended_list; +/* request type for signal handler */ +os_suspend_req_t g_req_type; +/* The thread which is processed */ +static osthread_t g_suspendee; +/* Semaphore used to inform signal sender about signal delivery */ +static sem_t g_yield_sem; + + +/* Forward declarations */ +static int suspend_init(); +static int suspend_init_lock(); +static os_thread_info_t* init_susres_list_item(); +static os_thread_info_t* suspend_add_thread(osthread_t thread); +static void suspend_remove_thread(osthread_t thread); +static os_thread_info_t* suspend_find_thread(osthread_t thread); +static void sigusr2_handler(int signum, siginfo_t* info, void* context); + + +/** + * Calculates absolute time in future for sem_timedwait timeout. + * @param ptime The pointer to time structure to fill + * @param delay Desired timeout in ns; not greater than 10^9 (1s) + */ +static inline __attribute__((always_inline)) +void get_exceed_time(struct timespec* ptime, long delay) +{ + clock_gettime(CLOCK_REALTIME, ptime); + + ptime->tv_nsec += delay; + if (ptime->tv_nsec >= 1000000000L) // overflow + { + ptime->tv_nsec -= 1000000000L; + ++ptime->tv_sec; + } +} + +/** + * Terminates the os thread. + */ +int port_thread_cancel(osthread_t os_thread) +{ + int status; + os_thread_info_t* pinfo; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(os_thread); + status = pthread_cancel(os_thread); + + if (pinfo && status == 0) + suspend_remove_thread(os_thread); + + pthread_mutex_unlock(&g_suspend_mutex); + return status; +} + +/** +* Sends a signal to a thread to make sure thread's write + * buffers are flushed. + */ +void port_thread_yield_other(osthread_t os_thread) { + struct timespec timeout; + os_thread_info_t* pinfo; + + if (!suspend_init_lock()) + return; + + pinfo = suspend_find_thread(os_thread); + + if (pinfo && pinfo->suspend_count > 0) { + pthread_mutex_unlock(&g_suspend_mutex); + return; + } + + g_suspendee = os_thread; + g_req_type = THREADREQ_YIELD; + + assert(os_thread); + if (pthread_kill(os_thread, SIGUSR2) == 0) { + // signal sent, let's do timed wait to make sure the signal + // was actually delivered + get_exceed_time(&timeout, 1000000L); + sem_timedwait(&g_yield_sem, &timeout); + } else { + if (pinfo) + suspend_remove_thread(os_thread); + } + + g_req_type = THREADREQ_NONE; + pthread_mutex_unlock(&g_suspend_mutex); +} + + +/** + * Suspend given thread + * @param thread The thread to suspend + */ +int port_thread_suspend(osthread_t thread) +{ + int status; + os_thread_info_t* pinfo; + + if (!thread) + return -1; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + pinfo = suspend_add_thread(thread); + + if (!pinfo) + { + pthread_mutex_unlock(&g_suspend_mutex); + return -1; + } + + if (pinfo->suspend_count > 0) + { + ++pinfo->suspend_count; + pthread_mutex_unlock(&g_suspend_mutex); + return 0; + } + + g_suspendee = thread; + g_req_type = THREADREQ_SUS; + + if (pthread_kill(thread, SIGUSR2) != 0) + { + suspend_remove_thread(thread); + pthread_mutex_unlock(&g_suspend_mutex); + return -1; + } + + /* Waiting for suspendee response */ + sem_wait(&pinfo->wake_sem); + /* Check result */ + status = (pinfo->suspend_count > 0) ? 0 : -1; + + pthread_mutex_unlock(&g_suspend_mutex); + return status; +} + +/** + * Resume given thread + * @param thread The thread to resume + */ +int port_thread_resume(osthread_t thread) +{ + int status; + os_thread_info_t* pinfo; + + if (!thread) + return -1; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + { + pthread_mutex_unlock(&g_suspend_mutex); + return -1; + } + + if (pinfo->suspend_count > 1) + { + --pinfo->suspend_count; + pthread_mutex_unlock(&g_suspend_mutex); + return 0; + } + + g_suspendee = thread; + g_req_type = THREADREQ_RES; + + if ((status = pthread_kill(thread, SIGUSR2)) != 0) + { + suspend_remove_thread(thread); + pthread_mutex_unlock(&g_suspend_mutex); + return status; + } + + /* Waiting for resume notification */ + sem_wait(&pinfo->wake_sem); + + suspend_remove_thread(thread); + + pthread_mutex_unlock(&g_suspend_mutex); + return 0; +} + +/** + * Determine suspend count for the given thread + * @param thread The thread to check + * @return -1 if error have occured + */ +int port_thread_get_suspend_count(osthread_t thread) +{ + os_thread_info_t* pinfo; + int suspend_count; + + if (!thread) + return -1; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(thread); + + suspend_count = pinfo ? pinfo->suspend_count : 0; + + pthread_mutex_unlock(&g_suspend_mutex); + return suspend_count; +} + +/** + * Get context for given thread + * @param thread The thread to process + * @param context Pointer to platform-dependant context structure + * @note The thread must be suspended + */ +int port_thread_get_context(osthread_t thread, thread_context_t *context) +{ + int status = -1; + os_thread_info_t* pinfo; + + if (!thread || !context) + return -1; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + { + pthread_mutex_unlock(&g_suspend_mutex); + return status; + } + + if (pinfo->suspend_count > 0) + { + *context = pinfo->context; + status = -1; + } + + pthread_mutex_unlock(&g_suspend_mutex); + return status; +} + +/** + * Set context for given thread + * @param thread The thread to process + * @param context Pointer to platform-dependant context structure + * @note The thread must be suspended + */ +int port_thread_set_context(osthread_t thread, thread_context_t *context) +{ + int status = -1; + os_thread_info_t* pinfo; + + if (!thread || !context) + return -1; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + { + pthread_mutex_unlock(&g_suspend_mutex); + return status; + } + + if (pinfo->suspend_count > 0) + { + pinfo->context = *context; + status = 0; + } + + pthread_mutex_unlock(&g_suspend_mutex); + return status; +} + + +static int suspend_init() +{ + static int initialized = 0; + struct sigaction sa; + static pthread_mutex_t suspend_init_mutex = PTHREAD_MUTEX_INITIALIZER; + + if (initialized) + return 1; + + pthread_mutex_lock(&suspend_init_mutex); + + if (!initialized) + { + /* Initialize all nesessary objects */ + int status; + pthread_mutex_t mut_init = PTHREAD_MUTEX_INITIALIZER; + + status = sem_init(&g_yield_sem, 0, 0); + + if (status != 0) + { + pthread_mutex_unlock(&suspend_init_mutex); + return 0; + } + + g_suspend_mutex = mut_init; + pthread_mutex_init(&g_suspend_mutex, NULL); + + g_suspended_list = NULL; + g_req_type = THREADREQ_NONE; + + /* set signal handler */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_SIGINFO | SA_RESTART; + sa.sa_sigaction = sigusr2_handler; + sigaction(SIGUSR2, &sa, NULL); + + initialized = 1; + } + + pthread_mutex_unlock(&suspend_init_mutex); + return 1; +} + +static int suspend_init_lock() +{ + if (!suspend_init()) + return 0; + + if (pthread_mutex_lock(&g_suspend_mutex) != 0) + return 0; + + return 1; +} + +static os_thread_info_t* init_susres_list_item() +{ + os_thread_info_t* pinfo = + (os_thread_info_t*)malloc(sizeof(os_thread_info_t)); + + if (pinfo == NULL) + return NULL; + + pinfo->suspend_count = 0; + + int status = sem_init(&pinfo->wake_sem, 0, 0); + + if (status != 0) + { + free(pinfo); + return NULL; + } + + return pinfo; +} + +static os_thread_info_t* suspend_add_thread(osthread_t thread) +{ + os_thread_info_t* pinfo = init_susres_list_item(); + + if (pinfo == NULL) + return NULL; + + pinfo->thread = thread; + pinfo->next = g_suspended_list; + g_suspended_list = pinfo; + return pinfo; +} + +static void suspend_remove_thread(osthread_t thread) +{ + os_thread_info_t** pprev = &g_suspended_list; + os_thread_info_t* pinfo; + + for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) + { + if (pinfo->thread == thread) + break; + + pprev = &pinfo->next; + } + + if (pinfo != NULL) + { + sem_destroy(&pinfo->wake_sem); + *pprev = pinfo->next; + free(pinfo); + } +} + +static os_thread_info_t* suspend_find_thread(osthread_t thread) +{ + os_thread_info_t* pinfo; + int status; + + for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) + { + if (pinfo->thread == thread) + break; + } + + return pinfo; +} + + +static void sigusr2_handler(int signum, siginfo_t* info, void* context) +{ + int status; + os_thread_info_t* pinfo; + + if (!suspend_init()) + return; + + if (signum != SIGUSR2) + return; + + /* We have g_suspend_mutex locked already */ + + if (g_req_type == THREADREQ_YIELD) + { + g_req_type = THREADREQ_NONE; + /* Inform requester */ + sem_post(&g_yield_sem); + return; + } + + if ((pinfo = suspend_find_thread(g_suspendee)) == NULL) + return; + + if (g_req_type == THREADREQ_SUS) + { + pinfo->suspend_count++; + g_req_type = THREADREQ_NONE; + memcpy(&pinfo->context, context, sizeof(ucontext_t)); + /* Inform suspender */ + sem_post(&pinfo->wake_sem); + + do + { + sigset_t sig_set; + sigemptyset(&sig_set); + sigsuspend(&sig_set); + + } while (pinfo->suspend_count > 0); + + /* We have returned from THREADREQ_RES handler */ + memcpy(context, &pinfo->context, sizeof(ucontext_t)); + /* Inform suspender */ + sem_post(&pinfo->wake_sem); + return; + } + else if (g_req_type == THREADREQ_RES) + { + pinfo->suspend_count--; + g_req_type = THREADREQ_NONE; + return; /* Return to interrupted THREADREQ_SUS handler */ + } +} diff --git a/vm/port/src/thread/win/thread_asm_em64t.asm b/vm/port/src/thread/win/thread_asm_em64t.asm index 1580d68..1424eec 100755 --- a/vm/port/src/thread/win/thread_asm_em64t.asm +++ b/vm/port/src/thread/win/thread_asm_em64t.asm @@ -41,11 +41,11 @@ _TEXT SEGMENT ; uint32 eflags;; 88h ; }; ; -; void transfer_to_regs(Registers* regs) +; void port_transfer_to_regs(Registers* regs) -PUBLIC transfer_to_regs +PUBLIC port_transfer_to_regs -transfer_to_regs PROC +port_transfer_to_regs PROC mov rdx, rcx ; regs pointer (1st param - RCX) -> RDX @@ -81,7 +81,48 @@ __skipefl__: mov rsp, qword ptr [rsp] ; load new RSP jmp qword ptr [rsp-88h] ; JMP to new RIP -transfer_to_regs ENDP +port_transfer_to_regs ENDP + + +; void port_longjump_stub(void) +; +; after returning from the called function, RSP points to the 2 argument +; slots in the stack. Saved Registers structure pointer is (RSP + 48) +; +; | interrupted | +; | program | <- RSP where the program was interrupted by exception +; |-------------| +; | 0x80 bytes | <- preserved stack area - we will not change it +; |-------------| +; | return addr | +; | from stub | <- for using in port_transfer_to_regs as [(new RSP) - 128 - 8] +; |-------------| +; | saved | +; | Registers | <- to restore register context +; |-------------| +; | [alignment] | <- align Regs pointer to 16-bytes boundary +; |-------------| +; | pointer to | +; | saved Regs | <- (RSP + 48) +; |-------------| +; | arg 5 | <- present even if not used +; |-------------| +; | arg 4 | <- present even if not used +; |-------------| +; | 32 bytes | <- 'red zone' for argument registers flushing +; |-------------| +; | return addr | +; | from 'fn' | <- address to return to the port_longjump_stub +; |-------------| + +PUBLIC port_longjump_stub + +port_longjump_stub PROC + + mov rcx, qword ptr [rsp + 48] ; load RCX with the address of saved Registers + call port_transfer_to_regs ; restore context + ret ; dummy RET - unreachable +port_longjump_stub ENDP _TEXT ENDS diff --git a/vm/port/src/thread/win/thread_asm_ia32.asm b/vm/port/src/thread/win/thread_asm_ia32.asm index 1b8241a..01e4b13 100755 --- a/vm/port/src/thread/win/thread_asm_ia32.asm +++ b/vm/port/src/thread/win/thread_asm_ia32.asm @@ -31,11 +31,11 @@ _TEXT SEGMENT PARA USE32 PUBLIC 'CODE' ; uint32 eflags; +24 ; }; ; -; void transfer_to_regs(Registers* regs) +; void port_transfer_to_regs(Registers* regs) -PUBLIC transfer_to_regs +PUBLIC port_transfer_to_regs -transfer_to_regs PROC +port_transfer_to_regs PROC mov edx, dword ptr [esp+04h] ; store regs pointer to EDX mov ebx, dword ptr [edx+20h] ; EIP field -> EBX @@ -59,7 +59,44 @@ _label_: mov esp, dword ptr [esp+04h] ; ((new ESP - 4) -> ESP ret ; JMP by RET -transfer_to_regs ENDP +port_transfer_to_regs ENDP + + +; void port_longjump_stub(void) +; +; after returning from the called function, EBP points to the pointer +; to saved Registers structure +; +; | interrupted | +; | program | <- ESP where the program was interrupted by exception +; |-------------| +; | return addr | +; | from stub | <- for using in port_transfer_to_regs +; |-------------| +; | saved | +; | Registers | <- to restore register context +; |-------------| +; | pointer to | +; | saved Regs | <- EBP +; |-------------| +; | arg 5 | <- +; |-------------| | +; ............... - arguments for 'fn' +; |-------------| | +; | arg 0 | <- +; |-------------| +; | return addr | +; | from 'fn' | <- address to return to the port_longjump_stub +; |-------------| + +PUBLIC port_longjump_stub + +port_longjump_stub PROC + + mov esp, ebp ; ESP now points to the address of saved Registers + call port_transfer_to_regs ; restore context + ret ; dummy RET - unreachable +port_longjump_stub ENDP _TEXT ENDS diff --git a/vm/port/src/thread/win/thread_em64t.c b/vm/port/src/thread/win/thread_em64t.c index 136ea28..b842d59 100644 --- a/vm/port/src/thread/win/thread_em64t.c +++ b/vm/port/src/thread/win/thread_em64t.c @@ -15,12 +15,10 @@ * limitations under the License. */ -#include -#include "platform_lowlevel.h" -#include "vm_core_types.h" +#include "port_thread.h" -void nt_to_vm_context(PCONTEXT pcontext, Registers* regs) +void port_thread_context_to_regs(Registers* regs, PCONTEXT pcontext) { regs->rsp = pcontext->Rsp; regs->rbp = pcontext->Rbp; @@ -45,7 +43,7 @@ void nt_to_vm_context(PCONTEXT pcontext, regs->eflags = pcontext->EFlags; } -void vm_to_nt_context(Registers* regs, PCONTEXT pcontext) +void port_thread_regs_to_context(PCONTEXT pcontext, Registers* regs) { pcontext->Rsp = regs->rsp; pcontext->Rbp = regs->rbp; @@ -70,28 +68,119 @@ void vm_to_nt_context(Registers* regs, P pcontext->EFlags = regs->eflags; } -// Max. 4 arguments can be set up -void regs_push_param(Registers* pregs, POINTER_SIZE_INT param, int num) -{ // RCX, RDX, R8, R9 - switch (num) - { - case 0: - pregs->rcx = param; - return; - case 1: - pregs->rdx = param; - return; - case 2: - pregs->r8 = param; - return; - case 3: - pregs->r9 = param; + +void port_longjump_stub(void); +#define DIR_FLAG ((uint32)0x00000400) + +void port_set_longjump_regs(void* fn, Registers* regs, int num, ...) +{ + void** sp; + va_list ap; + int i; + size_t align; + void** p_pregs; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + + if (!regs) return; + + sp = (void**)regs->rsp - 16 - 1; /* preserve 128-bytes area */ + *sp = (void*)regs->rip; + align = !((rcount & 1) ^ (((uint64)sp & sizeof(void*)) != 0)); + p_pregs = sp - rcount - align - 1; + sp = sp - rcount; + *((Registers*)sp) = *regs; + *p_pregs = (void*)sp; + + sp = p_pregs - 6 - 1; + + va_start(ap, num); + + if (num > 0) + { + void* arg = va_arg(ap, void*); + if (arg == regs) + regs->rcx = (uint64)(*p_pregs); /* Replace 1st arg */ + else + regs->rcx = (uint64)arg; + } + + if (num > 1) + regs->rdx = (uint64)va_arg(ap, void*); + + if (num > 2) + regs->r8 = (uint64)va_arg(ap, void*); + + if (num > 3) + regs->r9 = (uint64)va_arg(ap, void*); + + for (i = 5; i <= num; i = i + 1) + { + sp[i] = va_arg(ap, void*); } + + *sp = (void*)&port_longjump_stub; + regs->rsp = (uint64)sp; + regs->rip = (uint64)fn; + regs->eflags = regs->eflags & ~DIR_FLAG; } -void regs_push_return_address(Registers* pregs, void* ret_addr) +void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) { - pregs->rsp = pregs->rsp - 8; - *((void**)pregs->rsp) = ret_addr; + void** sp; + va_list ap; + int i; + size_t align; + void** p_pregs; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + Registers regs; + + if (!pregs) + return; + + regs = *pregs; + + sp = (void**)regs.rsp - 16 - 1; /* preserve 128-bytes area */ + *sp = (void*)regs.rip; + align = !((rcount & 1) ^ (((uint64)sp & sizeof(void*)) != 0)); + p_pregs = sp - rcount - align - 1; + sp = sp - rcount; + *((Registers*)sp) = regs; + *p_pregs = (void*)sp; + + sp = p_pregs - 6 - 1; + + va_start(ap, num); + + if (num > 0) + { + void* arg = va_arg(ap, void*); + if (arg == pregs) + regs.rcx = (uint64)(*p_pregs); /* Replace 1st arg */ + else + regs.rcx = (uint64)arg; + } + + if (num > 1) + regs.rdx = (uint64)va_arg(ap, void*); + + if (num > 2) + regs.r8 = (uint64)va_arg(ap, void*); + + if (num > 3) + regs.r9 = (uint64)va_arg(ap, void*); + + for (i = 5; i <= num; i = i + 1) + { + sp[i] = va_arg(ap, void*); + } + + *sp = (void*)&port_longjump_stub; + regs.rsp = (uint64)sp; + regs.rip = (uint64)fn; + regs.eflags = regs.eflags & ~DIR_FLAG; + + port_transfer_to_regs(®s); } diff --git a/vm/port/src/thread/win/thread_ia32.c b/vm/port/src/thread/win/thread_ia32.c new file mode 100755 index 0000000..d97c9d7 --- /dev/null +++ b/vm/port/src/thread/win/thread_ia32.c @@ -0,0 +1,134 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "port_thread.h" +#define ANSI +#include + + +void port_thread_context_to_regs(Registers* regs, PCONTEXT context) +{ + regs->eax = context->Eax; + regs->ecx = context->Ecx; + regs->edx = context->Edx; + regs->edi = context->Edi; + regs->esi = context->Esi; + regs->ebx = context->Ebx; + regs->ebp = context->Ebp; + regs->eip = context->Eip; + regs->esp = context->Esp; + regs->eflags = context->EFlags; +} + +void port_thread_regs_to_context(PCONTEXT context, Registers* regs) +{ + context->Esp = regs->esp; + context->Eip = regs->eip; + context->Ebp = regs->ebp; + context->Ebx = regs->ebx; + context->Esi = regs->esi; + context->Edi = regs->edi; + context->Eax = regs->eax; + context->Ecx = regs->ecx; + context->Edx = regs->edx; + context->EFlags = regs->eflags; +} + + +void port_longjump_stub(void); +#define DIR_FLAG ((uint32)0x00000400) + +void port_set_longjump_regs(void* fn, Registers* regs, int num, ...) +{ + void** sp; + va_list ap; + int i; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + + if (!regs) + return; + + sp = (void**)regs->esp - 1; + *sp = (void*)regs->eip; + sp = sp - rcount - 1; + *((Registers*)(sp + 1)) = *regs; + *sp = (void*)(sp + 1); + regs->ebp = (uint32)sp; + + sp = sp - num - 1; + + va_start(ap, num); + + for (i = 1; i <= num; i = i + 1) + { + void* arg = va_arg(ap, void*); + + if (i == 1 && arg == regs) + sp[i] = *((void**)regs->ebp); /* Replace 1st arg */ + else + sp[i] = arg; + } + + *sp = (void*)&port_longjump_stub; + regs->esp = (uint32)sp; + regs->eip = (uint32)fn; + regs->eflags = regs->eflags & ~DIR_FLAG; +} + +void port_transfer_to_function(void* fn, Registers* pregs, int num, ...) +{ + void** sp; + va_list ap; + int i; + size_t rcount = + (sizeof(Registers) + sizeof(void*) - 1) / sizeof(void*); + Registers regs; + + if (!pregs) + return; + + regs = *pregs; + + sp = (void**)regs.esp - 1; + *sp = (void*)regs.eip; + sp = sp - rcount - 1; + *((Registers*)(sp + 1)) = regs; + *sp = (void*)(sp + 1); + regs.ebp = (uint32)sp; + + sp = sp - num - 1; + + va_start(ap, num); + + for (i = 1; i <= num; i = i + 1) + { + void* arg = va_arg(ap, void*); + + if (i == 1 && arg == pregs) + sp[i] = *((void**)regs.ebp); /* Replace 1st arg */ + else + sp[i] = arg; + } + + *sp = (void*)&port_longjump_stub; + regs.esp = (uint32)sp; + regs.eip = (uint32)fn; + regs.eflags = regs.eflags & ~DIR_FLAG; + + port_transfer_to_regs(®s); +} diff --git a/vm/port/src/thread/win/thread_os.c b/vm/port/src/thread/win/thread_os.c new file mode 100755 index 0000000..f59b307 --- /dev/null +++ b/vm/port/src/thread/win/thread_os.c @@ -0,0 +1,371 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include +#include "port_thread.h" + + +typedef struct os_thread_info_t os_thread_info_t; + +struct os_thread_info_t +{ + osthread_t thread; + int suspend_count; + thread_context_t context; + + os_thread_info_t* next; +}; + + +static CRITICAL_SECTION g_crit_section; +static os_thread_info_t* g_suspended_list = NULL; + +/* Forward declarations */ +static int suspend_init_lock(); +static os_thread_info_t* init_susres_list_item(); +static os_thread_info_t* suspend_add_thread(osthread_t thread); +static void suspend_remove_thread(osthread_t thread); +static os_thread_info_t* suspend_find_thread(osthread_t thread); + + +/** + * Terminates the os thread. + */ +int port_thread_cancel(osthread_t os_thread) +{ + os_thread_info_t* pinfo; + int status = TM_ERROR_NONE; + + if (!suspend_init_lock()) + return TM_ERROR_INTERNAL; + + pinfo = suspend_find_thread(os_thread); + + if (pinfo) + suspend_remove_thread(os_thread); + + if (!TerminateThread(os_thread, 0)) + status = (int)GetLastError(); + + LeaveCriticalSection(&g_crit_section); + return status; +} + +/** + * Causes the other thread to have a memory barrier by suspending + * and resuming it. + */ +void port_thread_yield_other(osthread_t os_thread) +{ + os_thread_info_t* pinfo; + + /* + * Synchronization is needed to avoid cyclic (mutual) suspension problem. + * Accordingly to MSDN, it is possible on multiprocessor box that + * 2 threads suspend each other and become deadlocked. + */ + if (!suspend_init_lock()) // Initializes and enters a critical section + return; + + pinfo = suspend_find_thread(os_thread); + + if (pinfo && pinfo->suspend_count > 0) { + LeaveCriticalSection(&g_crit_section); + return; + } + + if (SuspendThread(os_thread) != -1) { + /* suspended successfully, so resume it back. */ + ResumeThread(os_thread); + } + + LeaveCriticalSection(&g_crit_section); +} + + +/** + * Suspend given thread + * @param thread The thread to suspend + */ +int port_thread_suspend(osthread_t thread) +{ + os_thread_info_t* pinfo; + DWORD old_count; + + if (!thread) + return TM_ERROR_NULL_POINTER; + + if (!suspend_init_lock()) + return TM_ERROR_INTERNAL; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + pinfo = suspend_add_thread(thread); + + if (!pinfo) + { + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_OUT_OF_MEMORY; + } + + if (pinfo->suspend_count > 0) + { + ++pinfo->suspend_count; + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_NONE; + } + + old_count = SuspendThread(thread); + + if (old_count == (DWORD)-1) + { + int status = (int)GetLastError(); + LeaveCriticalSection(&g_crit_section); + return status; + } + + ++pinfo->suspend_count; + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_NONE; +} + +/** + * Resume given thread + * @param thread The thread to resume + */ +int port_thread_resume(osthread_t thread) +{ + os_thread_info_t* pinfo; + DWORD old_count; + + if (!thread) + return TM_ERROR_NULL_POINTER; + + if (!suspend_init_lock()) + return TM_ERROR_INTERNAL; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + { + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_UNATTACHED_THREAD; + } + + if (pinfo->suspend_count > 1) + { + --pinfo->suspend_count; + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_NONE; + } + + old_count = ResumeThread(thread); + + if (old_count == (DWORD)-1) + { + int status = (int)GetLastError(); + LeaveCriticalSection(&g_crit_section); + return status; + } + + if (--pinfo->suspend_count == 0) + suspend_remove_thread(thread); + + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_NONE; +} + +/** + * Determine suspend count for the given thread + * @param thread The thread to check + * @return -1 if error have occured + */ +int port_thread_get_suspend_count(osthread_t thread) +{ + os_thread_info_t* pinfo; + int suspend_count; + + if (!thread) + return -1; + + if (!suspend_init_lock()) + return -1; + + pinfo = suspend_find_thread(thread); + suspend_count = pinfo ? pinfo->suspend_count : 0; + + LeaveCriticalSection(&g_crit_section); + return suspend_count; +} + +/** + * Get context for given thread + * @param thread The thread to process + * @param context Pointer to platform-dependant context structure + * @note The thread must be suspended + */ +int port_thread_get_context(osthread_t thread, thread_context_t *context) +{ + os_thread_info_t* pinfo; + CONTEXT local_context; + + if (!thread || !context) + return TM_ERROR_NULL_POINTER; + + if (!suspend_init_lock()) + return TM_ERROR_INTERNAL; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + { + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_UNATTACHED_THREAD; + } + +#ifdef CONTEXT_ALL + local_context.ContextFlags = CONTEXT_ALL; +#else + local_context.ContextFlags = CONTEXT_FULL; +#endif + + if (!GetThreadContext(thread, &local_context)) + { + int status = (int)GetLastError(); + LeaveCriticalSection(&g_crit_section); + return status; + } + + pinfo->context = local_context; + *context = local_context; + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_NONE; +} + +/** + * Set context for given thread + * @param thread The thread to process + * @param context Pointer to platform-dependant context structure + * @note The thread must be suspended + */ +int port_thread_set_context(osthread_t thread, thread_context_t *context) +{ + os_thread_info_t* pinfo; + + if (!thread || !context) + return -1; + + if (!suspend_init_lock()) + return -2; + + pinfo = suspend_find_thread(thread); + + if (!pinfo) + { + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_UNATTACHED_THREAD; + } + + if (!SetThreadContext(thread, context)) + { + int status = (int)GetLastError(); + LeaveCriticalSection(&g_crit_section); + return status; + } + + pinfo->context = *context; + LeaveCriticalSection(&g_crit_section); + return TM_ERROR_NONE; +} + + +static int suspend_init_lock() +{ + static uint32 initialized = 0; + + if (!initialized) + { + // Critical section should be initialized only once, + // do nothing in case someone else already initialized it. + if (apr_atomic_cas32((volatile uint32*)&initialized, 1, 0) == 0) + InitializeCriticalSectionAndSpinCount(&g_crit_section, 400); + } + + EnterCriticalSection(&g_crit_section); + return 1; +} + +static os_thread_info_t* init_susres_list_item() +{ + os_thread_info_t* pinfo = + (os_thread_info_t*)malloc(sizeof(os_thread_info_t)); + + if (pinfo) + pinfo->suspend_count = 0; + + return pinfo; +} + +static os_thread_info_t* suspend_add_thread(osthread_t thread) +{ + os_thread_info_t* pinfo = init_susres_list_item(); + + if (!pinfo) + return NULL; + + pinfo->thread = thread; + pinfo->next = g_suspended_list; + g_suspended_list = pinfo; + + return pinfo; +} + +static void suspend_remove_thread(osthread_t thread) +{ + os_thread_info_t** pprev = &g_suspended_list; + os_thread_info_t* pinfo; + + for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) + { + if (pinfo->thread == thread) + break; + + pprev = &pinfo->next; + } + + if (pinfo) + { + *pprev = pinfo->next; + free(pinfo); + } +} + +static os_thread_info_t* suspend_find_thread(osthread_t thread) +{ + os_thread_info_t* pinfo; + + for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) + { + if (pinfo->thread == thread) + break; + } + + return pinfo; +} + diff --git a/vm/thread/src/linux/os_thread.c b/vm/thread/src/linux/os_thread.c index 26c8a16..1e6d503 100644 --- a/vm/thread/src/linux/os_thread.c +++ b/vm/thread/src/linux/os_thread.c @@ -17,28 +17,15 @@ #include #include -#if defined(LINUX) -#include // gettid() -#endif #include // sched_param #include #include #include #include +#include "port_thread.h" #include "thread_private.h" -#ifdef LINUX -#ifdef _syscall0 -_syscall0(pid_t,gettid) -pid_t gettid(void); -#else -static pid_t gettid(void) -{ - return (pid_t)syscall(__NR_gettid); -} -#endif -#endif /** * Creates new thread. @@ -174,24 +161,6 @@ void os_thread_exit(IDATA status) } /** - * Calculates absolute time in future for sem_timedwait timeout. - * @param ptime The pointer to time structure to fill - * @param delay Desired timeout in ns; not greater than 10^9 (1s) - */ -static inline __attribute__((always_inline)) -void get_exceed_time(struct timespec* ptime, long delay) -{ - clock_gettime(CLOCK_REALTIME, ptime); - - ptime->tv_nsec += delay; - if (ptime->tv_nsec >= 1000000000L) // overflow - { - ptime->tv_nsec -= 1000000000L; - ++ptime->tv_sec; - } -} - -/** * Queries amount of user and kernel times consumed by the thread, * in nanoseconds. * @@ -238,480 +207,3 @@ UDATA os_get_foreign_thread_stack_size() return common_stack_size; } - - -typedef enum -{ - THREADREQ_NONE = 0, - THREADREQ_SUS = 1, - THREADREQ_RES = 2, - THREADREQ_YIELD = 3 -} os_suspend_req_t; - -typedef struct os_thread_info_t os_thread_info_t; - -struct os_thread_info_t -{ - osthread_t thread; - int suspend_count; - sem_t wake_sem; /* to sem_post from signal handler */ - os_thread_context_t context; - - os_thread_info_t* next; -}; - - -/* Global mutex to syncronize access to os_thread_info_t list */ -static pthread_mutex_t g_suspend_mutex; -/* Global list with suspended threads info */ -static os_thread_info_t* g_suspended_list; -/* request type for signal handler */ -os_suspend_req_t g_req_type; -/* The thread which is processed */ -static osthread_t g_suspendee; -/* Semaphore used to inform signal sender about signal delivery */ -static sem_t g_yield_sem; - - -/* Forward declarations */ -static int suspend_init(); -static int suspend_init_lock(); -static os_thread_info_t* init_susres_list_item(); -static os_thread_info_t* suspend_add_thread(osthread_t thread); -static void suspend_remove_thread(osthread_t thread); -static os_thread_info_t* suspend_find_thread(osthread_t thread); -static void sigusr2_handler(int signum, siginfo_t* info, void* context); - - -/** - * Terminates the os thread. - */ -int os_thread_cancel(osthread_t os_thread) -{ - int status; - os_thread_info_t* pinfo; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(os_thread); - status = pthread_cancel(os_thread); - - if (pinfo && status == 0) - suspend_remove_thread(os_thread); - - pthread_mutex_unlock(&g_suspend_mutex); - return status; -} - -/** -* Sends a signal to a thread to make sure thread's write - * buffers are flushed. - */ -void os_thread_yield_other(osthread_t os_thread) { - struct timespec timeout; - os_thread_info_t* pinfo; - - if (!suspend_init_lock()) - return; - - pinfo = suspend_find_thread(os_thread); - - if (pinfo && pinfo->suspend_count > 0) { - pthread_mutex_unlock(&g_suspend_mutex); - return; - } - - g_suspendee = os_thread; - g_req_type = THREADREQ_YIELD; - - assert(os_thread); - if (pthread_kill(os_thread, SIGUSR2) == 0) { - // signal sent, let's do timed wait to make sure the signal - // was actually delivered - get_exceed_time(&timeout, 1000000L); - sem_timedwait(&g_yield_sem, &timeout); - } else { - if (pinfo) - suspend_remove_thread(os_thread); - } - - g_req_type = THREADREQ_NONE; - pthread_mutex_unlock(&g_suspend_mutex); -} - - -/** - * Suspend given thread - * @param thread The thread to suspend - */ -int os_thread_suspend(osthread_t thread) -{ - int status; - os_thread_info_t* pinfo; - - if (!thread) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - pinfo = suspend_add_thread(thread); - - if (!pinfo) - { - pthread_mutex_unlock(&g_suspend_mutex); - return TM_ERROR_OUT_OF_MEMORY; - } - - if (pinfo->suspend_count > 0) - { - ++pinfo->suspend_count; - pthread_mutex_unlock(&g_suspend_mutex); - return TM_ERROR_NONE; - } - - g_suspendee = thread; - g_req_type = THREADREQ_SUS; - - if (pthread_kill(thread, SIGUSR2) != 0) - { - suspend_remove_thread(thread); - pthread_mutex_unlock(&g_suspend_mutex); - return TM_ERROR_INTERNAL; - } - - /* Waiting for suspendee response */ - sem_wait(&pinfo->wake_sem); - /* Check result */ - status = (pinfo->suspend_count > 0) ? TM_ERROR_NONE : TM_ERROR_INTERNAL; - - pthread_mutex_unlock(&g_suspend_mutex); - return status; -} - -/** - * Resume given thread - * @param thread The thread to resume - */ -int os_thread_resume(osthread_t thread) -{ - int status; - os_thread_info_t* pinfo; - - if (!thread) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - { - pthread_mutex_unlock(&g_suspend_mutex); - return TM_ERROR_UNATTACHED_THREAD; - } - - if (pinfo->suspend_count > 1) - { - --pinfo->suspend_count; - pthread_mutex_unlock(&g_suspend_mutex); - return TM_ERROR_NONE; - } - - g_suspendee = thread; - g_req_type = THREADREQ_RES; - - if ((status = pthread_kill(thread, SIGUSR2)) != 0) - { - suspend_remove_thread(thread); - pthread_mutex_unlock(&g_suspend_mutex); - return status; - } - - /* Waiting for resume notification */ - sem_wait(&pinfo->wake_sem); - - suspend_remove_thread(thread); - - pthread_mutex_unlock(&g_suspend_mutex); - return TM_ERROR_NONE; -} - -/** - * Determine suspend count for the given thread - * @param thread The thread to check - * @return -1 if error have occured - */ -int os_thread_get_suspend_count(osthread_t thread) -{ - os_thread_info_t* pinfo; - int suspend_count; - - if (!thread) - return -1; - - if (!suspend_init_lock()) - return -1; - - pinfo = suspend_find_thread(thread); - - suspend_count = pinfo ? pinfo->suspend_count : 0; - - pthread_mutex_unlock(&g_suspend_mutex); - return suspend_count; -} - -/** - * Get context for given thread - * @param thread The thread to process - * @param context Pointer to platform-dependant context structure - * @note The thread must be suspended - */ -int os_thread_get_context(osthread_t thread, os_thread_context_t *context) -{ - int status = TM_ERROR_UNATTACHED_THREAD; - os_thread_info_t* pinfo; - - if (!thread || !context) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - { - pthread_mutex_unlock(&g_suspend_mutex); - return status; - } - - if (pinfo->suspend_count > 0) - { - *context = pinfo->context; - status = TM_ERROR_NONE; - } - - pthread_mutex_unlock(&g_suspend_mutex); - return status; -} - -/** - * Set context for given thread - * @param thread The thread to process - * @param context Pointer to platform-dependant context structure - * @note The thread must be suspended - */ -int os_thread_set_context(osthread_t thread, os_thread_context_t *context) -{ - int status = TM_ERROR_UNATTACHED_THREAD; - os_thread_info_t* pinfo; - - if (!thread || !context) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - { - pthread_mutex_unlock(&g_suspend_mutex); - return status; - } - - if (pinfo->suspend_count > 0) - { - pinfo->context = *context; - status = TM_ERROR_NONE; - } - - pthread_mutex_unlock(&g_suspend_mutex); - return status; -} - - -static int suspend_init() -{ - static int initialized = 0; - struct sigaction sa; - static pthread_mutex_t suspend_init_mutex = PTHREAD_MUTEX_INITIALIZER; - - if (initialized) - return 1; - - pthread_mutex_lock(&suspend_init_mutex); - - if (!initialized) - { - /* Initialize all nesessary objects */ - int status; - pthread_mutex_t mut_init = PTHREAD_MUTEX_INITIALIZER; - - status = sem_init(&g_yield_sem, 0, 0); - - if (status != 0) - { - pthread_mutex_unlock(&suspend_init_mutex); - return 0; - } - - g_suspend_mutex = mut_init; - pthread_mutex_init(&g_suspend_mutex, NULL); - - g_suspended_list = NULL; - g_req_type = THREADREQ_NONE; - - /* set signal handler */ - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_SIGINFO | SA_RESTART; - sa.sa_sigaction = sigusr2_handler; - sigaction(SIGUSR2, &sa, NULL); - - initialized = 1; - } - - pthread_mutex_unlock(&suspend_init_mutex); - return 1; -} - -static int suspend_init_lock() -{ - if (!suspend_init()) - return 0; - - if (pthread_mutex_lock(&g_suspend_mutex) != 0) - return 0; - - return 1; -} - -static os_thread_info_t* init_susres_list_item() -{ - os_thread_info_t* pinfo = - (os_thread_info_t*)malloc(sizeof(os_thread_info_t)); - - if (pinfo == NULL) - return NULL; - - pinfo->suspend_count = 0; - - int status = sem_init(&pinfo->wake_sem, 0, 0); - - if (status != 0) - { - free(pinfo); - return NULL; - } - - return pinfo; -} - -static os_thread_info_t* suspend_add_thread(osthread_t thread) -{ - os_thread_info_t* pinfo = init_susres_list_item(); - - if (pinfo == NULL) - return NULL; - - pinfo->thread = thread; - pinfo->next = g_suspended_list; - g_suspended_list = pinfo; - return pinfo; -} - -static void suspend_remove_thread(osthread_t thread) -{ - os_thread_info_t** pprev = &g_suspended_list; - os_thread_info_t* pinfo; - - for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) - { - if (pinfo->thread == thread) - break; - - pprev = &pinfo->next; - } - - if (pinfo != NULL) - { - sem_destroy(&pinfo->wake_sem); - *pprev = pinfo->next; - free(pinfo); - } -} - -static os_thread_info_t* suspend_find_thread(osthread_t thread) -{ - os_thread_info_t* pinfo; - int status; - - for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) - { - if (pinfo->thread == thread) - break; - } - - return pinfo; -} - - -static void sigusr2_handler(int signum, siginfo_t* info, void* context) -{ - int status; - os_thread_info_t* pinfo; - - if (!suspend_init()) - return; - - if (signum != SIGUSR2) - return; - - /* We have g_suspend_mutex locked already */ - - if (g_req_type == THREADREQ_YIELD) - { - g_req_type = THREADREQ_NONE; - /* Inform requester */ - sem_post(&g_yield_sem); - return; - } - - if ((pinfo = suspend_find_thread(g_suspendee)) == NULL) - return; - - if (g_req_type == THREADREQ_SUS) - { - pinfo->suspend_count++; - g_req_type = THREADREQ_NONE; - memcpy(&pinfo->context, context, sizeof(ucontext_t)); - /* Inform suspender */ - sem_post(&pinfo->wake_sem); - - do - { - sigset_t sig_set; - sigemptyset(&sig_set); - sigsuspend(&sig_set); - - } while (pinfo->suspend_count > 0); - - /* We have returned from THREADREQ_RES handler */ - memcpy(context, &pinfo->context, sizeof(ucontext_t)); - /* Inform suspender */ - sem_post(&pinfo->wake_sem); - return; - } - else if (g_req_type == THREADREQ_RES) - { - pinfo->suspend_count--; - g_req_type = THREADREQ_NONE; - return; /* Return to interrupted THREADREQ_SUS handler */ - } -} diff --git a/vm/thread/src/thread_native_basic.c b/vm/thread/src/thread_native_basic.c index 3019d2b..6db927c 100644 --- a/vm/thread/src/thread_native_basic.c +++ b/vm/thread/src/thread_native_basic.c @@ -35,6 +35,7 @@ #endif //PLATFORM_POSIX #include #include +#include "port_thread.h" #include "thread_private.h" extern hythread_group_t TM_DEFAULT_GROUP; @@ -299,7 +300,7 @@ void VMCALL hythread_yield() { * @return none */ void VMCALL hythread_yield_other(hythread_t thread) { - os_thread_yield_other(thread->os_handle); + port_thread_yield_other(thread->os_handle); } /** @@ -520,7 +521,7 @@ IDATA VMCALL hythread_get_group(hythread void VMCALL hythread_cancel(hythread_t thread) { osthread_t os_handle = thread->os_handle; hythread_detach(thread); - os_thread_cancel(os_handle); + port_thread_cancel(os_handle); os_thread_join(os_handle); } diff --git a/vm/thread/src/thread_native_thin_monitor.c b/vm/thread/src/thread_native_thin_monitor.c index e222043..b7ca6ac 100644 --- a/vm/thread/src/thread_native_thin_monitor.c +++ b/vm/thread/src/thread_native_thin_monitor.c @@ -24,10 +24,11 @@ #undef LOG_DOMAIN #define LOG_DOMAIN "tm.locks" #include -#include "thread_private.h" #include #include #include "port_barriers.h" +#include "port_thread.h" +#include "thread_private.h" /** @name Thin monitors support. Implement thin-fat scheme. */ @@ -240,7 +241,7 @@ IDATA VMCALL hythread_unreserve_lock(hyt // resume owner if (owner) { - os_thread_yield_other(owner->os_handle); + port_thread_yield_other(owner->os_handle); hythread_resume(owner); } diff --git a/vm/thread/src/thread_ncai_common.c b/vm/thread/src/thread_ncai_common.c index 437d723..9339071 100644 --- a/vm/thread/src/thread_ncai_common.c +++ b/vm/thread/src/thread_ncai_common.c @@ -12,6 +12,7 @@ #include #include "thread_private.h" #include #include +#include "port_thread.h" /** @@ -29,7 +30,7 @@ IDATA VMCALL hythread_suspend_thread_nat assert(thread->os_handle); - return os_thread_suspend(thread->os_handle); + return port_thread_suspend(thread->os_handle); } /** @@ -47,7 +48,7 @@ IDATA VMCALL hythread_resume_thread_nati assert(thread->os_handle); - return os_thread_resume(thread->os_handle); + return port_thread_resume(thread->os_handle); } /** @@ -66,7 +67,7 @@ int VMCALL hythread_get_suspend_count_na assert(thread->os_handle); - return os_thread_get_suspend_count(thread->os_handle); + return port_thread_get_suspend_count(thread->os_handle); } /** @@ -75,7 +76,7 @@ int VMCALL hythread_get_suspend_count_na * @param[in] thread to get context. * @param[out] pointer to context structure. */ -IDATA VMCALL hythread_get_thread_context(hythread_t thread, os_thread_context_t* pcontext) +IDATA VMCALL hythread_get_thread_context(hythread_t thread, thread_context_t* pcontext) { if (pcontext == NULL || thread == NULL) return TM_ERROR_NULL_POINTER; @@ -85,7 +86,7 @@ IDATA VMCALL hythread_get_thread_context assert(thread->os_handle); - return os_thread_get_context(thread->os_handle, pcontext); + return port_thread_get_context(thread->os_handle, pcontext); } /** @@ -94,7 +95,7 @@ IDATA VMCALL hythread_get_thread_context * @param[in] thread to set context. * @param[in] pointer to context structure. */ -IDATA VMCALL hythread_set_thread_context(hythread_t thread, os_thread_context_t* pcontext) +IDATA VMCALL hythread_set_thread_context(hythread_t thread, thread_context_t* pcontext) { if (pcontext == NULL || thread == NULL) return TM_ERROR_NULL_POINTER; @@ -104,5 +105,5 @@ IDATA VMCALL hythread_set_thread_context assert(thread->os_handle); - return os_thread_get_context(thread->os_handle, pcontext); + return port_thread_get_context(thread->os_handle, pcontext); } diff --git a/vm/thread/src/thread_private.h b/vm/thread/src/thread_private.h index 8cc2bd3..adb673c 100644 --- a/vm/thread/src/thread_private.h +++ b/vm/thread/src/thread_private.h @@ -309,21 +309,14 @@ int os_thread_create(osthread_t* phandle hythread_wrapper_t func, void *data); int os_thread_set_priority(osthread_t thread, int priority); osthread_t os_thread_current(); -int os_thread_cancel(osthread_t); int os_thread_free(osthread_t os_thread); void os_thread_exit(IDATA status); int os_thread_join(osthread_t os_thread); -void os_thread_yield_other(osthread_t); int os_get_thread_times(osthread_t os_thread, int64* pkernel, int64* puser); int os_cond_timedwait(hycond_t *cond, hymutex_t *mutex, I_64 ms, IDATA nano); UDATA os_get_foreign_thread_stack_size(); -int os_thread_suspend(osthread_t thread); -int os_thread_resume(osthread_t thread); -int os_thread_get_suspend_count(osthread_t thread); -int os_thread_get_context(osthread_t thread, os_thread_context_t *context); -int os_thread_set_context(osthread_t thread, os_thread_context_t *context); #ifdef __cplusplus } diff --git a/vm/thread/src/win/os_thread.c b/vm/thread/src/win/os_thread.c index 68f30ee..5009b8c 100644 --- a/vm/thread/src/win/os_thread.c +++ b/vm/thread/src/win/os_thread.c @@ -174,352 +174,3 @@ UDATA os_get_foreign_thread_stack_size() return (UDATA)stack_size; } -typedef struct os_thread_info_t os_thread_info_t; - -struct os_thread_info_t -{ - osthread_t thread; - int suspend_count; - os_thread_context_t context; - - os_thread_info_t* next; -}; - - -static CRITICAL_SECTION g_crit_section; -static os_thread_info_t* g_suspended_list = NULL; - -/* Forward declarations */ -static int suspend_init_lock(); -static os_thread_info_t* init_susres_list_item(); -static os_thread_info_t* suspend_add_thread(osthread_t thread); -static void suspend_remove_thread(osthread_t thread); -static os_thread_info_t* suspend_find_thread(osthread_t thread); - - -/** - * Terminates the os thread. - */ -int os_thread_cancel(osthread_t os_thread) -{ - os_thread_info_t* pinfo; - int status = TM_ERROR_NONE; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(os_thread); - - if (pinfo) - suspend_remove_thread(os_thread); - - if (!TerminateThread(os_thread, 0)) - status = (int)GetLastError(); - - LeaveCriticalSection(&g_crit_section); - return status; -} - -/** - * Causes the other thread to have a memory barrier by suspending - * and resuming it. - */ -void os_thread_yield_other(osthread_t os_thread) -{ - os_thread_info_t* pinfo; - - /* - * Synchronization is needed to avoid cyclic (mutual) suspension problem. - * Accordingly to MSDN, it is possible on multiprocessor box that - * 2 threads suspend each other and become deadlocked. - */ - if (!suspend_init_lock()) // Initializes and enters a critical section - return; - - pinfo = suspend_find_thread(os_thread); - - if (pinfo && pinfo->suspend_count > 0) { - LeaveCriticalSection(&g_crit_section); - return; - } - - if (SuspendThread(os_thread) != -1) { - /* suspended successfully, so resume it back. */ - ResumeThread(os_thread); - } - - LeaveCriticalSection(&g_crit_section); -} - - -/** - * Suspend given thread - * @param thread The thread to suspend - */ -int os_thread_suspend(osthread_t thread) -{ - os_thread_info_t* pinfo; - DWORD old_count; - - if (!thread) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - pinfo = suspend_add_thread(thread); - - if (!pinfo) - { - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_OUT_OF_MEMORY; - } - - if (pinfo->suspend_count > 0) - { - ++pinfo->suspend_count; - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_NONE; - } - - old_count = SuspendThread(thread); - - if (old_count == (DWORD)-1) - { - int status = (int)GetLastError(); - LeaveCriticalSection(&g_crit_section); - return status; - } - - ++pinfo->suspend_count; - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_NONE; -} - -/** - * Resume given thread - * @param thread The thread to resume - */ -int os_thread_resume(osthread_t thread) -{ - os_thread_info_t* pinfo; - DWORD old_count; - - if (!thread) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - { - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_UNATTACHED_THREAD; - } - - if (pinfo->suspend_count > 1) - { - --pinfo->suspend_count; - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_NONE; - } - - old_count = ResumeThread(thread); - - if (old_count == (DWORD)-1) - { - int status = (int)GetLastError(); - LeaveCriticalSection(&g_crit_section); - return status; - } - - if (--pinfo->suspend_count == 0) - suspend_remove_thread(thread); - - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_NONE; -} - -/** - * Determine suspend count for the given thread - * @param thread The thread to check - * @return -1 if error have occured - */ -int os_thread_get_suspend_count(osthread_t thread) -{ - os_thread_info_t* pinfo; - int suspend_count; - - if (!thread) - return -1; - - if (!suspend_init_lock()) - return -1; - - pinfo = suspend_find_thread(thread); - suspend_count = pinfo ? pinfo->suspend_count : 0; - - LeaveCriticalSection(&g_crit_section); - return suspend_count; -} - -/** - * Get context for given thread - * @param thread The thread to process - * @param context Pointer to platform-dependant context structure - * @note The thread must be suspended - */ -int os_thread_get_context(osthread_t thread, os_thread_context_t *context) -{ - os_thread_info_t* pinfo; - CONTEXT local_context; - - if (!thread || !context) - return TM_ERROR_NULL_POINTER; - - if (!suspend_init_lock()) - return TM_ERROR_INTERNAL; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - { - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_UNATTACHED_THREAD; - } - -#ifdef CONTEXT_ALL - local_context.ContextFlags = CONTEXT_ALL; -#else - local_context.ContextFlags = CONTEXT_FULL; -#endif - - if (!GetThreadContext(thread, &local_context)) - { - int status = (int)GetLastError(); - LeaveCriticalSection(&g_crit_section); - return status; - } - - pinfo->context = local_context; - *context = local_context; - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_NONE; -} - -/** - * Set context for given thread - * @param thread The thread to process - * @param context Pointer to platform-dependant context structure - * @note The thread must be suspended - */ -int os_thread_set_context(osthread_t thread, os_thread_context_t *context) -{ - os_thread_info_t* pinfo; - - if (!thread || !context) - return -1; - - if (!suspend_init_lock()) - return -2; - - pinfo = suspend_find_thread(thread); - - if (!pinfo) - { - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_UNATTACHED_THREAD; - } - - if (!SetThreadContext(thread, context)) - { - int status = (int)GetLastError(); - LeaveCriticalSection(&g_crit_section); - return status; - } - - pinfo->context = *context; - LeaveCriticalSection(&g_crit_section); - return TM_ERROR_NONE; -} - - -static int suspend_init_lock() -{ - static uint32 initialized = 0; - - if (!initialized) - { - // Critical section should be initialized only once, - // do nothing in case someone else already initialized it. - if (apr_atomic_cas32((volatile uint32*)&initialized, 1, 0) == 0) - InitializeCriticalSectionAndSpinCount(&g_crit_section, 400); - } - - EnterCriticalSection(&g_crit_section); - return 1; -} - -static os_thread_info_t* init_susres_list_item() -{ - os_thread_info_t* pinfo = - (os_thread_info_t*)malloc(sizeof(os_thread_info_t)); - - if (pinfo) - pinfo->suspend_count = 0; - - return pinfo; -} - -static os_thread_info_t* suspend_add_thread(osthread_t thread) -{ - os_thread_info_t* pinfo = init_susres_list_item(); - - if (!pinfo) - return NULL; - - pinfo->thread = thread; - pinfo->next = g_suspended_list; - g_suspended_list = pinfo; - - return pinfo; -} - -static void suspend_remove_thread(osthread_t thread) -{ - os_thread_info_t** pprev = &g_suspended_list; - os_thread_info_t* pinfo; - - for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) - { - if (pinfo->thread == thread) - break; - - pprev = &pinfo->next; - } - - if (pinfo) - { - *pprev = pinfo->next; - free(pinfo); - } -} - -static os_thread_info_t* suspend_find_thread(osthread_t thread) -{ - os_thread_info_t* pinfo; - - for (pinfo = g_suspended_list; pinfo; pinfo = pinfo->next) - { - if (pinfo->thread == thread) - break; - } - - return pinfo; -} - diff --git a/vm/vmcore/include/exceptions_jit.h b/vm/vmcore/include/exceptions_jit.h index 3e9ca3f..e8b87fc 100644 --- a/vm/vmcore/include/exceptions_jit.h +++ b/vm/vmcore/include/exceptions_jit.h @@ -93,8 +93,4 @@ NativeCodePtr exn_get_rth_throw_illegal_ Class_Handle exn_get_class_cast_exception_type(); -// Exception catch callback for jvm ti support implementation -extern "C" void asm_exception_catch_callback(); -extern "C" void asm_jvmti_exception_catch_callback(); - #endif // _EXCEPTIONS_JIT_H_ diff --git a/vm/vmcore/include/jvmti_break_intf.h b/vm/vmcore/include/jvmti_break_intf.h index 9696fd8..66a672c 100644 --- a/vm/vmcore/include/jvmti_break_intf.h +++ b/vm/vmcore/include/jvmti_break_intf.h @@ -146,7 +146,7 @@ public: VMBreakPoint* get_next_breakpoint(VMBreakPoint* prev); // General callback functions - void process_native_breakpoint(); + void process_native_breakpoint(Registers* regs); jbyte process_interpreter_breakpoint(jmethodID method, jlocation location); // Find thread-local breakpoint information @@ -186,8 +186,6 @@ public: // Basic operations - // 'data' must be allocated with JVMTI Allocate (or internal _allocate) - // Users must not deallocate 'data', it will be deallocated by 'remove' VMBreakPointRef* add_reference(jmethodID method, jlocation location, POINTER_SIZE_INT data); // To specify address explicitly VMBreakPointRef* add_reference(jmethodID method, jlocation location, @@ -231,7 +229,7 @@ private: }; // Address of this function is used for stack unwinding througn breakpoint -extern "C" void __cdecl process_native_breakpoint_event(); +extern "C" void __cdecl process_native_breakpoint_event(Registers* regs); // Callback function for native breakpoint processing bool jvmti_jit_breakpoint_handler(Registers *regs); diff --git a/vm/vmcore/include/vm_threads.h b/vm/vmcore/include/vm_threads.h index 30cf023..9227f95 100644 --- a/vm/vmcore/include/vm_threads.h +++ b/vm/vmcore/include/vm_threads.h @@ -79,7 +79,7 @@ void jthread_end_count(); jint jthread_allocate_vm_thread_pool(JavaVM * java_vm, vm_thread_t vm_thread); void jthread_deallocate_vm_thread_pool(vm_thread_t vm_thread); vm_thread_t jthread_allocate_thread(); -void vm_set_jvmti_saved_exception_registers(vm_thread_t vm_thread, Registers & regs); +void vm_set_jvmti_saved_exception_registers(vm_thread_t vm_thread, Registers* regs); void vm_set_exception_registers(vm_thread_t vm_thread, Registers & regs); void *vm_get_ip_from_regs(vm_thread_t vm_thread); void vm_reset_ip_from_regs(vm_thread_t vm_thread); diff --git a/vm/vmcore/src/jvmti/jvmti_break_intf.cpp b/vm/vmcore/src/jvmti/jvmti_break_intf.cpp index 3f096c3..240e8f9 100644 --- a/vm/vmcore/src/jvmti/jvmti_break_intf.cpp +++ b/vm/vmcore/src/jvmti/jvmti_break_intf.cpp @@ -593,20 +593,18 @@ VMBreakPoints::find_thread_local_break(V } void -VMBreakPoints::process_native_breakpoint() +VMBreakPoints::process_native_breakpoint(Registers* regs) { #if (defined _IA32_) || (defined _EM64T_) // When we get here we know already that breakpoint occurred in JITted code, // JVMTI handles it, and registers context is saved for us in TLS VM_thread *vm_thread = p_TLS_vmthread; lock(); - Registers regs = - *(Registers*)vm_thread->jvmti_thread.jvmti_saved_exception_registers; - NativeCodePtr addr = (NativeCodePtr)regs.get_ip(); + NativeCodePtr addr = (NativeCodePtr)regs->get_ip(); TRACE2("jvmti.break", "Native breakpoint occured: " << addr); - M2nFrame* m2nf = m2n_push_suspended_frame(®s); + M2nFrame* m2nf = m2n_push_suspended_frame(regs); VMBreakPoint* bp = find_breakpoint(addr); if (NULL == bp) { @@ -618,7 +616,7 @@ #if (defined _IA32_) || (defined _EM64T_ // instrumented, it means that another breakpoint has been set // there right after unlock was done. m2n_set_last_frame(m2n_get_previous_frame(m2nf)); - transfer_to_regs(®s); + return; } assert(bp->addr == addr); TRACE2("jvmti.break", "Process native breakpoint: " @@ -629,6 +627,9 @@ #if (defined _IA32_) || (defined _EM64T_ << (bp->method ? method_get_descriptor((Method*)bp->method) : "") << " :" << bp->location << " :" << bp->addr); + // Save registers in TLS - mainly for single stepping in virtual methods + vm_set_jvmti_saved_exception_registers(vm_thread, regs); + jbyte *instruction_buffer; BEGIN_RAISE_AREA; @@ -672,7 +673,7 @@ #if (defined _IA32_) || (defined _EM64T_ { local.intf = intf->m_next; VMBreakPoint local_bp = *bp; - local_bp.regs = regs; + local_bp.regs = *regs; local.local_bp = &local_bp; // Set local copy's pointer to local copy of disassembler local_bp.disasm = &idisasm; @@ -724,10 +725,6 @@ #if (defined _IA32_) || (defined _EM64T_ } } - // Registers in TLS can be changed in user callbacks - // It should be restored to keep original address of instrumented instruction - // Exception/signal handlers use it when HWE occurs in instruction buffer - vm_set_jvmti_saved_exception_registers(vm_thread, regs); unlock(); // Now we need to return back to normal code execution, it is @@ -807,7 +804,7 @@ #if (defined _IA32_) || (defined _EM64T_ } case InstructionDisassembler::INDIRECT_JUMP: { - char *jump_target = (char *)idisasm.get_target_address_from_context(®s); + char *jump_target = (char *)idisasm.get_target_address_from_context(regs); // Create JMP to the absolute address which conditional jump // had in the execution buffer @@ -817,7 +814,7 @@ #if (defined _IA32_) || (defined _EM64T_ case InstructionDisassembler::INDIRECT_CALL: { jbyte *next_instruction = interrupted_instruction + instruction_length; - char *jump_target = (char *)idisasm.get_target_address_from_context(®s); + char *jump_target = (char *)idisasm.get_target_address_from_context(regs); char *code = (char *)instruction_buffer; // Push "return address" to the $next_instruction @@ -836,8 +833,8 @@ #if (defined _IA32_) || (defined _EM64T_ // execute the original instruction with the registers which it // had before breakpoint happened m2n_set_last_frame(m2n_get_previous_frame(m2nf)); - regs.set_ip(instruction_buffer); - transfer_to_regs(®s); + regs->set_ip(instruction_buffer); + return; // We'll go to updated regs #else // PLATFORM dependent code abort(); @@ -1349,41 +1346,16 @@ static bool clear_native_breakpoint(VMBr ////////////////////////////////////////////////////////////////////////////// -void __cdecl process_native_breakpoint_event() +void __cdecl process_native_breakpoint_event(Registers* regs) { DebugUtilsTI *ti = VM_Global_State::loader_env->TI; - ti->vm_brpt->process_native_breakpoint(); + ti->vm_brpt->process_native_breakpoint(regs); } -#if defined (_WIN32) -#if defined(_EM64T_) -extern "C" void asm_process_native_breakpoint_event(); -#else -static void __declspec(naked) -asm_process_native_breakpoint_event() -{ - __asm { - push ebp - mov ebp,esp - pushfd - cld - call process_native_breakpoint_event - popfd - pop ebp - ret - } -} -#endif -#endif // _WIN32 bool jvmti_jit_breakpoint_handler(Registers *regs) { -#if PLATFORM_POSIX && INSTRUMENTATION_BYTE == INSTRUMENTATION_BYTE_INT3 - // Int3 exception address points to the instruction after it - NativeCodePtr native_location = (NativeCodePtr)(((POINTER_SIZE_INT)regs->get_ip()) - 1); -#else NativeCodePtr native_location = (NativeCodePtr)regs->get_ip(); -#endif TRACE2("jvmti.break", "BREAKPOINT occured: " << native_location); @@ -1426,16 +1398,8 @@ #if 0 } #endif - // Store possibly corrected location - regs->set_ip((void*)native_location); - // Copy original registers to TLS - vm_set_jvmti_saved_exception_registers(vm_thread, *regs); - // Set return address for exception handler -#if defined (PLATFORM_POSIX) - regs->set_ip((void*)process_native_breakpoint_event); -#else // PLATFORM_POSIX - regs->set_ip((void*)asm_process_native_breakpoint_event); -#endif //PLATFORM_POSIX + NativeCodePtr callback = (NativeCodePtr)process_native_breakpoint_event; + port_set_longjump_regs(callback, regs, 1, regs); return true; } diff --git a/vm/vmcore/src/ncai/utils/ncai_utils_em64t.cpp b/vm/vmcore/src/ncai/utils/ncai_utils_em64t.cpp index bcb07ef..8609324 100644 --- a/vm/vmcore/src/ncai/utils/ncai_utils_em64t.cpp +++ b/vm/vmcore/src/ncai/utils/ncai_utils_em64t.cpp @@ -180,7 +180,7 @@ #endif // #ifdef PLATFORM_POSIX bool ncai_get_register_value(hythread_t thread, jint reg_number, void* buf_ptr) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) @@ -199,7 +199,7 @@ bool ncai_get_register_value(hythread_t bool ncai_set_register_value(hythread_t thread, jint reg_number, void* buf_ptr) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) @@ -222,7 +222,7 @@ bool ncai_set_register_value(hythread_t void* ncai_get_instruction_pointer(hythread_t thread) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) diff --git a/vm/vmcore/src/ncai/utils/ncai_utils_ia32.cpp b/vm/vmcore/src/ncai/utils/ncai_utils_ia32.cpp index 76d8404..98d03f8 100644 --- a/vm/vmcore/src/ncai/utils/ncai_utils_ia32.cpp +++ b/vm/vmcore/src/ncai/utils/ncai_utils_ia32.cpp @@ -153,7 +153,7 @@ #endif // #ifdef PLATFORM_POSIX bool ncai_get_register_value(hythread_t thread, jint reg_number, void* buf_ptr) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) @@ -172,7 +172,7 @@ bool ncai_get_register_value(hythread_t bool ncai_set_register_value(hythread_t thread, jint reg_number, void* buf_ptr) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) @@ -195,7 +195,7 @@ bool ncai_set_register_value(hythread_t void* ncai_get_instruction_pointer(hythread_t thread) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) diff --git a/vm/vmcore/src/ncai/utils/ncai_utils_ipf.cpp b/vm/vmcore/src/ncai/utils/ncai_utils_ipf.cpp index e0e3662..f4b98d2 100644 --- a/vm/vmcore/src/ncai/utils/ncai_utils_ipf.cpp +++ b/vm/vmcore/src/ncai/utils/ncai_utils_ipf.cpp @@ -203,7 +203,7 @@ static void ncai_registers_to_context(Nc bool ncai_get_register_value(hythread_t thread, jint reg_number, void* buf_ptr) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) @@ -222,7 +222,7 @@ bool ncai_get_register_value(hythread_t bool ncai_set_register_value(hythread_t thread, jint reg_number, void* buf_ptr) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) @@ -245,7 +245,7 @@ bool ncai_set_register_value(hythread_t void* ncai_get_instruction_pointer(hythread_t thread) { - os_thread_context_t context; + thread_context_t context; IDATA status = hythread_get_thread_context(thread, &context); if (status != TM_ERROR_NONE) diff --git a/vm/vmcore/src/ncai/utils/ncai_utils_linux.cpp b/vm/vmcore/src/ncai/utils/ncai_utils_linux.cpp index 163f47b..9537f96 100644 --- a/vm/vmcore/src/ncai/utils/ncai_utils_linux.cpp +++ b/vm/vmcore/src/ncai/utils/ncai_utils_linux.cpp @@ -5,9 +5,7 @@ #include "open/ncai_thread.h" #include "ncai_internal.h" - -void ucontext_to_regs(Registers* regs, ucontext_t *uc); -void regs_to_ucontext(ucontext_t *uc, Registers* regs); +#include "port_thread.h" bool ncai_get_generic_registers(hythread_t thread, Registers* regs) { @@ -20,6 +18,6 @@ bool ncai_get_generic_registers(hythread if (status != TM_ERROR_NONE) return false; - ucontext_to_regs(regs, &uc); + port_thread_context_to_regs(regs, &uc); return true; } diff --git a/vm/vmcore/src/ncai/utils/ncai_utils_win.cpp b/vm/vmcore/src/ncai/utils/ncai_utils_win.cpp index 236fcfe..b8c53a7 100644 --- a/vm/vmcore/src/ncai/utils/ncai_utils_win.cpp +++ b/vm/vmcore/src/ncai/utils/ncai_utils_win.cpp @@ -4,11 +4,9 @@ */ #include "open/ncai_thread.h" +#include "port_thread.h" #include "ncai_internal.h" -void nt_to_vm_context(PCONTEXT pcontext, Registers* regs); -void vm_to_nt_context(Registers* regs, PCONTEXT pcontext); - bool ncai_get_generic_registers(hythread_t thread, Registers* regs) { if (regs == NULL) @@ -21,6 +19,6 @@ bool ncai_get_generic_registers(hythread if (status != TM_ERROR_NONE) return false; - nt_to_vm_context(&context, regs); + port_thread_context_to_regs(regs, &context); return true; } diff --git a/vm/vmcore/src/thread/thread_manager.cpp b/vm/vmcore/src/thread/thread_manager.cpp index 1119051..f459eb0 100644 --- a/vm/vmcore/src/thread/thread_manager.cpp +++ b/vm/vmcore/src/thread/thread_manager.cpp @@ -166,7 +166,7 @@ vm_thread_t jthread_allocate_thread() * Sets resisters to JVMTI thread */ void vm_set_jvmti_saved_exception_registers(vm_thread_t vm_thread, - Registers & regs) + Registers* regs) { assert(vm_thread); jvmti_thread_t jvmti_thread = &vm_thread->jvmti_thread; @@ -175,7 +175,7 @@ void vm_set_jvmti_saved_exception_regist (Registers*)STD_MALLOC(sizeof(Registers)); assert(jvmti_thread->jvmti_saved_exception_registers); } - *(jvmti_thread->jvmti_saved_exception_registers) = regs; + *(jvmti_thread->jvmti_saved_exception_registers) = *regs; } // vm_set_jvmti_saved_exception_registers /** diff --git a/vm/vmcore/src/util/linux/crash_handler.cpp b/vm/vmcore/src/util/linux/crash_handler.cpp index 4204996..16b0bf1 100644 --- a/vm/vmcore/src/util/linux/crash_handler.cpp +++ b/vm/vmcore/src/util/linux/crash_handler.cpp @@ -29,6 +29,7 @@ #include "environment.h" #include "port_sysinfo.h" #include "platform_lowlevel.h" #include "exception_filter.h" +#include "port_thread.h" #include "crash_handler.h" diff --git a/vm/vmcore/src/util/linux/ia32_em64t/signals_common.cpp b/vm/vmcore/src/util/linux/ia32_em64t/signals_common.cpp old mode 100644 new mode 100755 index 45d8d3b..a9c6ffa --- a/vm/vmcore/src/util/linux/ia32_em64t/signals_common.cpp +++ b/vm/vmcore/src/util/linux/ia32_em64t/signals_common.cpp @@ -42,6 +42,7 @@ #if defined(FREEBSD) #include #endif #include +#include "port_thread.h" #include "method_lookup.h" #include "Class.h" @@ -74,58 +75,44 @@ static void general_crash_handler(int si extern "C" { -static void DECL_CHANDLER c_exception_handler(Class* exn_class, bool java_code) { +static void DECL_CHANDLER c_exception_handler(Registers* regs, Class* exn_class, bool java_code) { // this exception handler is executed *after* NT exception handler returned DebugUtilsTI* ti = VM_Global_State::loader_env->TI; // Create local copy for registers because registers in TLS can be changed - Registers regs = {0}; VM_thread *thread = p_TLS_vmthread; assert(thread); assert(exn_class); - if (thread->regs) { - regs = *thread->regs; - } - - exn_athrow_regs(®s, exn_class, java_code, true); + exn_athrow_regs(regs, exn_class, java_code, true); } } -static void throw_from_sigcontext(ucontext_t *uc, Class* exc_clss) +static void throw_from_sigcontext(Registers* regs, Class* exc_clss) { - Registers regs; - ucontext_to_regs(®s, uc); - DebugUtilsTI* ti = VM_Global_State::loader_env->TI; - bool java_code = (vm_identify_eip((void *)regs.get_ip()) == VM_TYPE_JAVA); + bool java_code = (vm_identify_eip((void*)regs->get_ip()) == VM_TYPE_JAVA); VM_thread* vmthread = p_TLS_vmthread; - NativeCodePtr callback = (NativeCodePtr) c_exception_handler; - vm_set_exception_registers(vmthread, regs); - add_red_zone(®s); - regs_push_param(®s, java_code, 1/*2nd arg */); + vm_set_exception_registers(vmthread, *regs); + assert(exc_clss); - regs_push_param(®s, (POINTER_SIZE_INT)exc_clss, 0/* 1st arg */); - // To get proper stack alignment on x86_64 - regs_align_stack(®s); - // imitate return IP on stack - regs_push_return_address(®s, NULL); - - // set up the real exception handler address - regs.set_ip(callback); - regs_to_ucontext(uc, ®s); + + NativeCodePtr callback = (NativeCodePtr) c_exception_handler; + // Set up parameters and registers + port_set_longjump_regs(callback, regs, + 3, regs, exc_clss, (POINTER_SIZE_INT)java_code); } -static bool java_throw_from_sigcontext(ucontext_t *uc, Class* exc_clss) +static bool java_throw_from_sigcontext(Registers* regs, Class* exc_clss) { ASSERT_NO_INTERPRETER; - void* ip = (void*)UC_IP(uc); + void* ip = regs->get_ip(); VM_Code_Type vmct = vm_identify_eip(ip); if(vmct != VM_TYPE_JAVA) { return false; } - throw_from_sigcontext(uc, exc_clss); + throw_from_sigcontext(regs, exc_clss); return true; } @@ -359,7 +346,7 @@ void remove_guard_stack(vm_thread_t vm_t vm_thread->restore_guard_page = true; } -static bool check_stack_overflow(siginfo_t *info, ucontext_t *uc) { +static bool check_stack_overflow(Registers* regs, void* fault_addr) { char* stack_addr = (char*) get_stack_addr(); size_t stack_size = get_stack_size(); size_t guard_stack_size = get_guard_stack_size(); @@ -371,17 +358,13 @@ static bool check_stack_overflow(siginfo // FIXME: Workaround for main thread guard_page_end += guard_page_size; - char* fault_addr = (char*)(info->si_addr); - //char* esp_value = (char*)(uc->uc_mcontext.gregs[REG_ESP]); - return((guard_page_begin <= fault_addr) && (fault_addr < guard_page_end)); } -static void stack_overflow_handler(int signum, siginfo_t* UNREF info, void* context) +static void stack_overflow_handler(Registers* regs) { - ucontext_t *uc = (ucontext_t *)context; Global_Env *env = VM_Global_State::loader_env; vm_thread_t vm_thread = p_TLS_vmthread; @@ -389,7 +372,7 @@ static void stack_overflow_handler(int s vm_thread->restore_guard_page = true; if (java_throw_from_sigcontext( - uc, env->java_lang_StackOverflowError_Class)) { + regs, env->java_lang_StackOverflowError_Class)) { return; } else { if (is_unwindable()) { @@ -397,7 +380,7 @@ static void stack_overflow_handler(int s tmn_suspend_disable(); } throw_from_sigcontext( - uc, env->java_lang_StackOverflowError_Class); + regs, env->java_lang_StackOverflowError_Class); } else { exn_raise_by_class(env->java_lang_StackOverflowError_Class); } @@ -405,91 +388,76 @@ static void stack_overflow_handler(int s } -static void null_java_reference_handler(int signum, siginfo_t* UNREF info, void* context) +static void null_java_reference_handler(int signum, Registers* regs, void* fault_addr) { - ucontext_t *uc = (ucontext_t *)context; VM_thread *vm_thread = p_TLS_vmthread; - Registers regs; - ucontext_to_regs(®s, uc); if (vm_thread && vm_thread->jvmti_thread.violation_flag) { vm_thread->jvmti_thread.violation_flag = 0; - regs.set_ip(vm_thread->jvmti_thread.violation_restart_address); - regs_to_ucontext(uc, ®s); + regs->set_ip(vm_thread->jvmti_thread.violation_restart_address); return; } Global_Env *env = VM_Global_State::loader_env; - TRACE2("signals", "NPE or SOE detected at " << (void*)UC_IP(uc)); + TRACE2("signals", "NPE or SOE detected at " << regs->get_ip()); - if (check_stack_overflow(info, uc)) { - stack_overflow_handler(signum, info, context); + if (check_stack_overflow(regs, fault_addr)) { + stack_overflow_handler(regs); return; } if (!interpreter_enabled()) { if (java_throw_from_sigcontext( - uc, env->java_lang_NullPointerException_Class)) { + regs, env->java_lang_NullPointerException_Class)) { return; } } - general_crash_handler(signum, ®s); + general_crash_handler(signum, regs); } -static void null_java_divide_by_zero_handler(int signum, siginfo_t* UNREF info, void* context) +static void null_java_divide_by_zero_handler(int signum, Registers* regs, void* UNREF fault_addr) { - ucontext_t *uc = (ucontext_t *)context; Global_Env *env = VM_Global_State::loader_env; TRACE2("signals", - "ArithmeticException detected at " << (void*)UC_IP(uc)); + "ArithmeticException detected at " << regs->get_ip()); if (!interpreter_enabled()) { if (java_throw_from_sigcontext( - uc, env->java_lang_ArithmeticException_Class)) { + regs, env->java_lang_ArithmeticException_Class)) { return; } } - Registers regs; - ucontext_to_regs(®s, uc); - general_crash_handler(signum, ®s); + general_crash_handler(signum, regs); } -static void jvmti_jit_breakpoint_handler(int signum, siginfo_t* UNREF info, void* context) +static void jvmti_jit_breakpoint_handler(int signum, Registers* regs, void* UNREF fault_addr) { - ucontext_t *uc = (ucontext_t*)context; - Registers regs; - - ucontext_to_regs(®s, uc); - TRACE2("signals", "JVMTI breakpoint detected at " << (void*)regs.get_ip()); + TRACE2("signals", "JVMTI breakpoint detected at " << (void*)regs->get_ip()); if (!interpreter_enabled()) { - bool handled = jvmti_jit_breakpoint_handler(®s); + regs->set_ip((void*)((POINTER_SIZE_INT)regs->get_ip() - 1)); + bool handled = jvmti_jit_breakpoint_handler(regs); if (handled) - { - regs_to_ucontext(uc, ®s); return; - } } - general_crash_handler(signum, ®s); + general_crash_handler(signum, regs); } /** * Print out the call stack of the aborted thread. * @note call stacks may be used for debugging */ -static void abort_handler (int signum, siginfo_t* UNREF info, void* context) +static void abort_handler(int signum, Registers* regs, void* UNREF fault_addr) { - Registers regs; - ucontext_to_regs(®s, (ucontext_t *)context); - general_crash_handler(signum, ®s); + general_crash_handler(signum, regs); } static void process_crash(Registers* regs) @@ -535,7 +503,8 @@ static void general_crash_handler(int si if (!is_gdb_crash_handler_enabled() || !gdb_crash_handler(regs)) { - process_crash(regs); + NativeCodePtr callback = (NativeCodePtr)process_crash; + port_set_longjump_regs(callback, regs, 1, regs); } } @@ -543,12 +512,13 @@ static void general_signal_handler(int s { bool replaced = false; ucontext_t* uc = (ucontext_t *)context; + void* fault_addr = info->si_addr; VM_thread* vm_thread = p_TLS_vmthread; bool violation = (signum == SIGSEGV) && vm_thread && vm_thread->jvmti_thread.violation_flag; Registers regs; - ucontext_to_regs(®s, uc); + port_thread_context_to_regs(®s, uc); void* saved_ip = regs.get_ip(); void* new_ip = saved_ip; bool in_java = false; @@ -570,7 +540,7 @@ static void general_signal_handler(int s replaced = true; new_ip = vm_get_ip_from_regs(vm_thread); regs.set_ip(new_ip); - regs_to_ucontext(uc, ®s); + port_thread_regs_to_context(uc, ®s); } in_java = (vm_identify_eip(regs.get_ip()) == VM_TYPE_JAVA); @@ -589,25 +559,25 @@ static void general_signal_handler(int s (!in_java && signum != SIGSEGV)) && signum != SIGTRAP && signum != SIGINT && signum != SIGQUIT) { - general_crash_handler(signum, ®s); regs.set_ip(saved_ip); - regs_to_ucontext(uc, ®s); + general_crash_handler(signum, ®s); + port_thread_regs_to_context(uc, ®s); return; } switch (signum) { case SIGTRAP: - jvmti_jit_breakpoint_handler(signum, info, context); + jvmti_jit_breakpoint_handler(signum, ®s, fault_addr); break; case SIGSEGV: - null_java_reference_handler(signum, info, context); + null_java_reference_handler(signum, ®s, fault_addr); break; case SIGFPE: - null_java_divide_by_zero_handler(signum, info, context); + null_java_divide_by_zero_handler(signum, ®s, fault_addr); break; case SIGABRT: - abort_handler(signum, info, context); + abort_handler(signum, ®s, fault_addr); break; case SIGINT: vm_interrupt_handler(signum); @@ -621,15 +591,13 @@ static void general_signal_handler(int s break; } - ucontext_to_regs(®s, uc); - // If IP was not changed in specific handler to start another handler, // we should restore original IP, if it's nesessary if (replaced && regs.get_ip() == new_ip) - { regs.set_ip((void*)saved_ip); - regs_to_ucontext(uc, ®s); - } + + // Update OS context + port_thread_regs_to_context(uc, ®s); } void initialize_signals() diff --git a/vm/vmcore/src/util/linux/include/platform_lowlevel.h b/vm/vmcore/src/util/linux/include/platform_lowlevel.h index d605ec3..ea463a4 100644 --- a/vm/vmcore/src/util/linux/include/platform_lowlevel.h +++ b/vm/vmcore/src/util/linux/include/platform_lowlevel.h @@ -50,25 +50,6 @@ #define __declspec(junk) #define _MAX_PATH PATH_MAX -#if defined(LINUX) - -#include -#include -#include - -#ifdef _syscall0 -pid_t gettid(void); -#else // _syscall0 -#include -#include -#define gettid() ((pid_t)syscall(__NR_gettid)) -#endif - -#else // !LINUX -#define gettid() getpid() -#endif - - #ifdef __cplusplus } #endif diff --git a/vm/vmcore/src/util/linux/include/signals_common.h b/vm/vmcore/src/util/linux/include/signals_common.h index a77f058..521d031 100644 --- a/vm/vmcore/src/util/linux/include/signals_common.h +++ b/vm/vmcore/src/util/linux/include/signals_common.h @@ -29,23 +29,6 @@ #endif #ifdef _EM64T_ // vvvvvvvv EM64T vvvvvvvv -// Prototype: POINTER_SIZE_INT& UC_IP(ucontext_t* uc) -#define UC_IP(uc) (uc->uc_mcontext.gregs[REG_RIP]) - -inline void add_red_zone(Registers* pregs) {pregs->rsp -= 0x80;} - -void regs_push_param(Registers* pregs, POINTER_SIZE_INT param, int UNREF num); - -inline void regs_push_return_address(Registers* pregs, void* ret_addr) { - pregs->rsp = pregs->rsp - 8; - *((void**)pregs->rsp) = ret_addr; -} - -inline void regs_align_stack(Registers* pregs) { - pregs->rsp = pregs->rsp - 8; - *((void**)pregs->rsp) = 0; -} - inline size_t get_mem_protect_stack_size() { return 0x0400; } inline size_t get_restore_stack_size() { @@ -58,27 +41,6 @@ #define DECL_CHANDLER __attribute__ ((us #else // #ifdef _EM64T_ // vvvvvvvv IA-32 vvvvvvvv -#if defined(LINUX) -// Prototype: POINTER_SIZE_INT& UC_IP(ucontext_t* uc) -#define UC_IP(uc) (uc->uc_mcontext.gregs[REG_EIP]) -#elif defined(FREEBSD) -#define UC_IP(uc) (uc->uc_mcontext.mc_eip) -#endif - -inline void add_red_zone(Registers* pregs) {} - -inline void regs_push_param(Registers* pregs, POINTER_SIZE_INT param, int UNREF num) { - pregs->esp = pregs->esp - 4; - *((uint32*)pregs->esp) = param; -} - -inline void regs_push_return_address(Registers* pregs, void* ret_addr) { - pregs->esp = pregs->esp - 4; - *((void**)pregs->esp) = ret_addr; -} - -inline void regs_align_stack(Registers* pregs) {} - inline size_t get_mem_protect_stack_size() { return 0x0100; } inline size_t get_restore_stack_size() { @@ -101,9 +63,4 @@ #define STACK_MMAP_ATTRS \ #endif - -void ucontext_to_regs(Registers* regs, ucontext_t *uc); -void regs_to_ucontext(ucontext_t *uc, Registers* regs); - - #endif // _SIGNALS_COMMON_H_ diff --git a/vm/vmcore/src/util/linux/os_wrapper.cpp b/vm/vmcore/src/util/linux/os_wrapper.cpp index 8fc0b97..aa8dc96 100644 --- a/vm/vmcore/src/util/linux/os_wrapper.cpp +++ b/vm/vmcore/src/util/linux/os_wrapper.cpp @@ -50,9 +50,3 @@ #error -- which uses SIGUSR1, SIGUSR2 (which conflicts with the java app debugger and vm) #endif #endif - -#ifdef LINUX -#ifdef _syscall0 -_syscall0(pid_t, gettid) -#endif -#endif diff --git a/vm/vmcore/src/util/linux/signals_ipf.cpp b/vm/vmcore/src/util/linux/signals_ipf.cpp index c0710fd..7585124 100644 --- a/vm/vmcore/src/util/linux/signals_ipf.cpp +++ b/vm/vmcore/src/util/linux/signals_ipf.cpp @@ -75,33 +75,6 @@ #include "jvmti_break_intf.h" #include -void ucontext_to_regs(Registers* regs, ucontext_t* uc) -{ - memcpy(regs->gr, uc->uc_mcontext.sc_gr, sizeof(regs->gr)); - memcpy(regs->fp, uc->uc_mcontext.sc_fr, sizeof(regs->fp)); - memcpy(regs->br, uc->uc_mcontext.sc_br, sizeof(regs->br)); - - regs->preds = uc->uc_mcontext.sc_pr; - regs->nats = uc->uc_mcontext.sc_ar_rnat; - regs->pfs = uc->uc_mcontext.sc_ar_pfs; - regs->bsp = (uint64*)uc->uc_mcontext.sc_ar_bsp; - regs->ip = uc->uc_mcontext.sc_ip; -} - -void regs_to_ucontext(ucontext_t* uc, Registers* regs) -{ - memcpy(uc->uc_mcontext.sc_gr, regs->gr, sizeof(regs->gr)); - memcpy(uc->uc_mcontext.sc_fr, regs->fp, sizeof(regs->fp)); - memcpy(uc->uc_mcontext.sc_br, regs->br, sizeof(regs->br)); - - uc->uc_mcontext.sc_pr = regs->preds; - uc->uc_mcontext.sc_ar_rnat = regs->nats; - uc->uc_mcontext.sc_ar_pfs = regs->pfs; - uc->uc_mcontext.sc_ar_bsp = (uint64)regs->bsp; - uc->uc_mcontext.sc_ip = regs->ip; -} - - void asm_exception_catch_callback() { // FIXME: not implemented fprintf(stderr, "FIXME: asm_jvmti_exception_catch_callback: not implemented\n"); @@ -129,7 +102,7 @@ void abort_handler (int signum, siginfo_ fprintf(stderr, "SIGABRT in VM code.\n"); Registers regs; ucontext_t *uc = (ucontext_t *)context; - ucontext_to_regs(®s, uc); + port_thread_context_to_regs(®s, uc); // setup default handler signal(signum, SIG_DFL); @@ -169,7 +142,7 @@ void null_java_divide_by_zero_handler(in fprintf(stderr, "SIGFPE in VM code.\n"); Registers regs; - ucontext_to_regs(®s, uc); + port_thread_context_to_regs(®s, uc); // setup default handler signal(signum, SIG_DFL); @@ -435,7 +408,7 @@ void null_java_reference_handler(int sig ucontext_t *uc = (ucontext_t *)context; Global_Env *env = VM_Global_State::loader_env; Registers regs; - ucontext_to_regs(®s, uc); + port_thread_context_to_regs(®s, uc); if (!p_TLS_vmthread) { diff --git a/vm/vmcore/src/util/linux/stack_dump_platf.cpp b/vm/vmcore/src/util/linux/stack_dump_platf.cpp index c278196..1bfd0c6 100644 --- a/vm/vmcore/src/util/linux/stack_dump_platf.cpp +++ b/vm/vmcore/src/util/linux/stack_dump_platf.cpp @@ -27,6 +27,7 @@ #include "open/hythread_ext.h" #include "native_stack.h" #include "port_filepath.h" #include "port_dso.h" +#include "port_thread.h" #include "stack_dump.h" diff --git a/vm/vmcore/src/util/native_stack.cpp b/vm/vmcore/src/util/native_stack.cpp index 41578e3..25ee12e 100644 --- a/vm/vmcore/src/util/native_stack.cpp +++ b/vm/vmcore/src/util/native_stack.cpp @@ -82,11 +82,15 @@ bool native_is_ip_stub(void* ip) return (native_find_stub(ip) != NULL); } +/* +Now the technique for calling C handler from a signal/exception context +guarantees that all needed return addresses are present in stack, so +there is no need in special processing static bool native_is_ip_in_breakpoint_handler(void* ip) { return (ip >= &process_native_breakpoint_event && ip < &jvmti_jit_breakpoint_handler); -} +}*/ /// Helper functions ////////////////////////////////////////////////////////////////////////////// @@ -311,13 +315,16 @@ static int walk_native_stack_jit( VMBreakPoints* vm_breaks = VM_Global_State::loader_env->TI->vm_brpt; vm_breaks->lock(); - +/* +Now the technique for calling C handler from a signal/exception context +guarantees that all needed return addresses are present in stack, so +there is no need in special processing if (native_is_ip_in_breakpoint_handler(tmp_regs.get_ip())) { regs = *pthread->jvmti_thread.jvmti_saved_exception_registers; flag_breakpoint = true; } - else + else*/ regs = tmp_regs; vm_breaks->unlock(); @@ -433,10 +440,13 @@ static int walk_native_stack_interpreter VMBreakPoints* vm_breaks = VM_Global_State::loader_env->TI->vm_brpt; vm_breaks->lock(); - +/* +Now the technique for calling C handler from a signal/exception context +guarantees that all needed return addresses are present in stack, so +there is no need in special processing if (native_is_ip_in_breakpoint_handler(tmp_regs.get_ip())) regs = *pthread->jvmti_thread.jvmti_saved_exception_registers; - else + else*/ regs = tmp_regs; vm_breaks->unlock(); diff --git a/vm/vmcore/src/util/win/em64t/exception_handlers.asm b/vm/vmcore/src/util/win/em64t/exception_handlers.asm index a98b697..b54aa4f 100644 --- a/vm/vmcore/src/util/win/em64t/exception_handlers.asm +++ b/vm/vmcore/src/util/win/em64t/exception_handlers.asm @@ -1,12 +1,6 @@ PUBLIC vectored_exception_handler EXTRN vectored_exception_handler_internal:PROC -PUBLIC asm_c_exception_handler -EXTRN c_exception_handler:PROC - -PUBLIC asm_process_native_breakpoint_event -EXTRN process_native_breakpoint_event:PROC - _TEXT SEGMENT vectored_exception_handler PROC @@ -28,56 +22,6 @@ vectored_exception_handler PROC vectored_exception_handler ENDP - -asm_c_exception_handler PROC - -; void __cdecl asm_c_exception_handler(Class *exn_class, bool in_java) -; void __cdecl c_exception_handler(Class* exn_class, bool in_java) -; Args: -; rcx - nt_exception -; rdx - in_java (actually in dl) -; r8 - none -; r9 - none - - pushfq - cld - push rax - push rbx - push rcx - push rdx - push r8 - push r9 - push r10 - push r11 - sub rsp, 32 ; allocate stack for 4 registers - call c_exception_handler - add rsp, 32 - pop r11 - pop r10 - pop r9 - pop r8 - pop rdx - pop rcx - pop rbx - pop rax - popfq - ret - -asm_c_exception_handler ENDP - -asm_process_native_breakpoint_event PROC - - pushfq - cld - sub rsp, 32 ; allocate stack for 4 registers - call process_native_breakpoint_event - add rsp, 32 - popfq - ret - -asm_process_native_breakpoint_event ENDP - - _TEXT ENDS END diff --git a/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp b/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp index e130e5c..feef001 100644 --- a/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp +++ b/vm/vmcore/src/util/win/ia32/nt_exception_filter.cpp @@ -15,41 +15,11 @@ * limitations under the License. */ -#include #include "platform_lowlevel.h" -#include "vm_core_types.h" #include "exceptions_jit.h" #include "exception_filter.h" -void nt_to_vm_context(PCONTEXT context, Registers* regs) -{ - regs->eax = context->Eax; - regs->ecx = context->Ecx; - regs->edx = context->Edx; - regs->edi = context->Edi; - regs->esi = context->Esi; - regs->ebx = context->Ebx; - regs->ebp = context->Ebp; - regs->eip = context->Eip; - regs->esp = context->Esp; - regs->eflags = context->EFlags; -} - -void vm_to_nt_context(Registers* regs, PCONTEXT context) -{ - context->Esp = regs->esp; - context->Eip = regs->eip; - context->Ebp = regs->ebp; - context->Ebx = regs->ebx; - context->Esi = regs->esi; - context->Edi = regs->edi; - context->Eax = regs->eax; - context->Ecx = regs->ecx; - context->Edx = regs->edx; - context->EFlags = regs->eflags; -} - LONG __declspec(naked) NTAPI vectored_exception_handler(LPEXCEPTION_POINTERS nt_exception) { __asm { @@ -65,39 +35,3 @@ LONG __declspec(naked) NTAPI vectored_ex ret 4 } } - -void __declspec(naked) asm_c_exception_handler(Class *exn_class, bool in_java) -{ - __asm { - push ebp - mov ebp,esp - pushfd - cld - mov eax, [ebp + 12] - push eax - mov eax, [ebp + 8] - push eax - call c_exception_handler - add esp, 8 - popfd - pop ebp - ret - } -} - -void* regs_get_sp(Registers* pregs) -{ - return (void*)pregs->esp; -} - -void regs_push_param(Registers* pregs, POINTER_SIZE_INT param, int UNREF num) -{ - pregs->esp = pregs->esp - 4; - *((uint32*)pregs->esp) = param; -} - -void regs_push_return_address(Registers* pregs, void* ret_addr) -{ - pregs->esp = pregs->esp - 4; - *((void**)pregs->esp) = ret_addr; -} diff --git a/vm/vmcore/src/util/win/ia32_em64t/nt_exception_filter_common.cpp b/vm/vmcore/src/util/win/ia32_em64t/nt_exception_filter_common.cpp index 4b3c417..3e82e6a 100644 --- a/vm/vmcore/src/util/win/ia32_em64t/nt_exception_filter_common.cpp +++ b/vm/vmcore/src/util/win/ia32_em64t/nt_exception_filter_common.cpp @@ -27,6 +27,7 @@ #include "stack_dump.h" #include "jvmti_break_intf.h" #include "m2n.h" #include "open/hythread_ext.h" +#include "port_thread.h" // Windows specific #include @@ -334,6 +335,7 @@ bool check_stack_size_enough_for_excepti return get_restore_stack_size() < available_stack_size; } + LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception) { DWORD code = nt_exception->ExceptionRecord->ExceptionCode; @@ -343,7 +345,7 @@ LONG NTAPI vectored_exception_handler_in VM_thread* vmthread = p_TLS_vmthread; // Convert NT context to Registers - nt_to_vm_context(context, ®s); + port_thread_context_to_regs(®s, context); POINTER_SIZE_INT saved_eip = (POINTER_SIZE_INT)regs.get_ip(); bool in_java = false; @@ -361,7 +363,7 @@ LONG NTAPI vectored_exception_handler_in { flag_replaced = true; regs.set_ip(vm_get_ip_from_regs(vmthread)); - vm_to_nt_context(®s, context); + port_thread_regs_to_context(context, ®s); } in_java = (vm_identify_eip(regs.get_ip()) == VM_TYPE_JAVA); @@ -399,12 +401,12 @@ LONG NTAPI vectored_exception_handler_in { LONG result = process_crash(®s, nt_exception, code); regs.set_ip((void*)saved_eip); - vm_to_nt_context(®s, context); + port_thread_regs_to_context(context, ®s); return result; } TRACE2("signals", ("VEH received an exception: code = %x, ip = %p, sp = %p", - nt_exception->ExceptionRecord->ExceptionCode, regs.get_ip(), regs_get_sp(®s))); + nt_exception->ExceptionRecord->ExceptionCode, regs.get_ip(), regs.get_sp())); // if HWE occured in java code, suspension should also have been disabled bool ncai_enabled = GlobalNCAI::isEnabled(); @@ -421,7 +423,7 @@ LONG NTAPI vectored_exception_handler_in { TRACE2("signals", ("StackOverflowError detected at ip = %p, esp = %p", - regs.get_ip(), regs_get_sp(®s))); + regs.get_ip(), regs.get_sp())); vmthread->restore_guard_page = true; exn_class = env->java_lang_StackOverflowError_Class; @@ -440,7 +442,7 @@ LONG NTAPI vectored_exception_handler_in // Mark raised exception in TLS and resume execution exn_raise_by_class(env->java_lang_StackOverflowError_Class); regs.set_ip((void*)saved_eip); - vm_to_nt_context(®s, context); + port_thread_regs_to_context(context, ®s); return EXCEPTION_CONTINUE_EXECUTION; } } @@ -471,7 +473,7 @@ LONG NTAPI vectored_exception_handler_in bool handled = jvmti_jit_breakpoint_handler(®s); if (handled) { - vm_to_nt_context(®s, context); + port_thread_regs_to_context(context, ®s); return EXCEPTION_CONTINUE_EXECUTION; } else @@ -481,7 +483,7 @@ LONG NTAPI vectored_exception_handler_in // unexpected hardware exception occured in java code LONG result = process_crash(®s, nt_exception, code); regs.set_ip((void*)saved_eip); - vm_to_nt_context(®s, context); + port_thread_regs_to_context(context, ®s); return result; } @@ -497,40 +499,30 @@ LONG NTAPI vectored_exception_handler_in // into thread-local registers snapshot vm_set_exception_registers(vmthread, regs); - // __cdecl <=> push parameters in the reversed order - // push in_java argument onto stack - regs_push_param(®s, in_java, 1/*2nd arg */); - // push the exn_class argument onto stack assert(exn_class); - regs_push_param(®s, (POINTER_SIZE_INT)exn_class, 0/* 1st arg */); - // imitate return IP on stack - regs_push_return_address(®s, NULL); - - // set up the real exception handler address - regs.set_ip(asm_c_exception_handler); + // Set up parameters and registers for C handler + port_set_longjump_regs(c_exception_handler, ®s, + 3, ®s, exn_class, (POINTER_SIZE_INT)in_java); // Store changes into NT context - vm_to_nt_context(®s, context); + port_thread_regs_to_context(context, ®s); // exit NT exception handler and transfer // control to VM exception handler return EXCEPTION_CONTINUE_EXECUTION; } -void __cdecl c_exception_handler(Class* exn_class, bool in_java) +void __cdecl c_exception_handler(Registers* regs, Class* exn_class, bool in_java) { // this exception handler is executed *after* NT exception handler returned DebugUtilsTI* ti = VM_Global_State::loader_env->TI; // Create local copy for registers because registers in TLS can be changed - Registers regs = {0}; VM_thread *thread = p_TLS_vmthread; assert(thread); - if (thread->regs) { - regs = *(Registers*)thread->regs; - } + assert(exn_class); TRACE2("signals", ("should throw exception %p at IP=%p, SP=%p", - exn_class, regs.get_ip(), regs_get_sp(®s))); - exn_athrow_regs(®s, exn_class, in_java, true); + exn_class, regs->get_ip(), regs->get_sp())); + exn_athrow_regs(regs, exn_class, in_java, true); assert(!"si_transfer_control should not return"); } diff --git a/vm/vmcore/src/util/win/include/exception_filter.h b/vm/vmcore/src/util/win/include/exception_filter.h index 1e7bb59..503eee2 100644 --- a/vm/vmcore/src/util/win/include/exception_filter.h +++ b/vm/vmcore/src/util/win/include/exception_filter.h @@ -36,17 +36,12 @@ LONG NTAPI vectored_exception_handler(LP LONG NTAPI vectored_exception_handler_internal(LPEXCEPTION_POINTERS nt_exception); // Function to throw exception -void __cdecl c_exception_handler(Class* exn_class, bool in_java); -// Assembler wrapper for c_exception_handler; is used to clear direction flag -void asm_c_exception_handler(Class *exn_class, bool in_java); +void __cdecl c_exception_handler(Registers* regs, Class* exn_class, bool in_java); // exception catch callback to restore stack after Stack Overflow Error void __cdecl exception_catch_callback_wrapper(); // exception catch support for JVMTI - void __cdecl jvmti_exception_catch_callback_wrapper(); -// Assembler wrappers; are used to restore registers -//void asm_exception_catch_callback(); // Declared in exceptions_jit.h -//void asm_jvmti_exception_catch_callback(); // Declared in exceptions_jit.h +void __cdecl jvmti_exception_catch_callback_wrapper(); #ifdef __cplusplus } // extern "C" @@ -56,15 +51,6 @@ #endif // Prints register state void print_reg_state(Registers* regs); -// Conversion from NT context to VM Registers structure and visa versa -void nt_to_vm_context(PCONTEXT context, Registers* regs); -void vm_to_nt_context(Registers* regs, PCONTEXT context); - -// Fuctions to manipulate with Registers structure -void* regs_get_sp(Registers* pregs); -void regs_push_param(Registers* pregs, POINTER_SIZE_INT param, int num); -void regs_push_return_address(Registers* pregs, void* ret_addr); - #endif // nt_exception_filter_h -- 1.3.3