diff options
author | Tor Lillqvist <tlillqvist@novell.com> | 2011-01-28 16:30:54 +0200 |
---|---|---|
committer | Tor Lillqvist <tlillqvist@novell.com> | 2011-01-28 16:41:04 +0200 |
commit | 66787671fa9635f2fb0bd021c21d87b22d007015 (patch) | |
tree | a0e4856f9aec9f66afe8d81da306c02c806db6eb /bridges | |
parent | 878b50cd6806598ba68fe428ccccc023034ba561 (diff) |
More work on x64 Windows C++-UNO bridge
Now the call through the trampoline into cpp_vtable_call() seems to
work, but I get a crash later. Glitches in parameter passing, no
doubt. Debugging needed in cpp_vtable_call() and cpp2uno_call().
The basic implementation is probably sane. But I wonder if I after all
should have done like in the x86-64 Linux implementation, with the
dynamically generated trampoline just jumping into fixed code shared
between all trampolines. Probably should redo it like that, yes.
Will it then cause a problem for OS unwinding if the caller of the
trampoline calls a short dynamically generated code snippet, which
then jumps into the fixed part, and only the fixed part has a
(assembler-generated) function table and unwind info? Probably not.
It is quite impossible that such a short dynamically generated snippet
with just a couple of instructions would cause an exception, and when
we have jumped into the fixed part, where the call to
cpp_vtable_call() is done, it doesn't matter any more that the caller
in fact didn't call what the function table claims is the entry
point. Or does it?
Doing it that way would mean no RtlAddFunctionTable() and
RtlDeleteFunctionTable() would be needed, and especially doing the
latter correctly is a bit hairy.
Diffstat (limited to 'bridges')
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm | 69 | ||||
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx | 160 | ||||
-rw-r--r-- | bridges/source/cpp_uno/shared/vtablefactory.cxx | 25 |
3 files changed, 124 insertions, 130 deletions
diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm b/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm index b1a4f7390..c02b667d2 100644 --- a/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm @@ -95,10 +95,10 @@ extern cpp_vtable_call: proc fp_spill_templates: public fp_spill_templates - movsd qword ptr 32[rsp], xmm3 - movsd qword ptr 24[rsp], xmm2 - movsd qword ptr 16[rsp], xmm1 - movsd qword ptr 8[rsp], xmm0 + movsd qword ptr (40+32)[rsp], xmm3 + movsd qword ptr (40+24)[rsp], xmm2 + movsd qword ptr (40+16)[rsp], xmm1 + movsd qword ptr (40+8)[rsp], xmm0 fp_spill_templates_end: public fp_spill_templates_end @@ -106,6 +106,40 @@ public fp_spill_templates_end trampoline_template proc + ;; Make stack frame. Re-align RSP at 16 bytes. We need just one + ;; qword of stack for our own purposes: Where cpp_vtable_call() + ;; will store the return value of the UNO callee. But we of course + ;; must also allocate space for the functions we call (i.e., just + ;; cpp_vtable_call()) to spill their register parameters. + + sub rsp, 40 +trampoline_template_prolog_end: + + jmp trampoline_template_spill_params + + ;; We store the function table (with just one entry, for this function) and + ;; the associated unwind info here at a known offset from the function start + ;; so that we don't have to know separately where it is when we need to unregister it, but + ;; can just use the known offset from the function start. + + align 4 + ;; See http://msdn.microsoft.com/en-us/library/ssa62fwe%28v=VS.90%29.aspx +trampoline_template_function_table:: +public trampoline_template_function_table + dword 0 + dword trampoline_template_end - trampoline_template + dword unwind_info - trampoline_template +unwind_info: + byte 1 + byte trampoline_template_prolog_end - trampoline_template + byte 1 + byte 0 + byte trampoline_template_prolog_end - trampoline_template + byte 42h + +trampoline_template_spill_params:: +public trampoline_template_spill_params + ;; Spill our register parameters. In the x64 Windows calling ;; convention the caller always has stack space allocated ;; where the callee can spill register parameters. @@ -114,26 +148,16 @@ trampoline_template proc ;; generated code snippet with floating-point moves for ;; floating-point parameters. - mov qword ptr 32[rsp], r9 + mov qword ptr (40+32)[rsp], r9 nop - mov qword ptr 24[rsp], r8 + mov qword ptr (40+24)[rsp], r8 nop - mov qword ptr 16[rsp], rdx + mov qword ptr (49+16)[rsp], rdx nop - mov qword ptr 8[rsp], rcx + mov qword ptr (40+8)[rsp], rcx nop -trampoline_template_spill_end:: -public trampoline_template_spill_end - - ;; Make stack frame. Re-align RSP at 16 bytes. We need just one - ;; qword of stack for our own purposes: Where cpp_vtable_call() - ;; will store the return value of the UNO callee. But we of course - ;; must also allocate space for the functions we call (i.e., just - ;; cpp_vtable_call()) to spill their register parameters. - - sub rsp, 40 -trampoline_template_prolog_end:: -public trampoline_template_prolog_end +trampoline_template_spill_params_end:: +public trampoline_template_spill_params_end ;; Call cpp_vtable_call() with 3 parameters: @@ -155,7 +179,10 @@ public trampoline_template_vtable_offset lea r8, 32[rsp] ;; Where cpp_vtable_call() will store the return value - call cpp_vtable_call ;; Actual address generated by codeSnippet() + mov rax, 12345467890abcdeh ;; cpp_vtable_call address, filled in by codeSnippet() +trampoline_template_cpp_vtable_call:: +public trampoline_template_cpp_vtable_call + call rax ;; cpp_vtable_call() returns the typelib_TypeClass type of the ;; return value of the called UNO function diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx b/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx index 615668b64..74abccde8 100644 --- a/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx @@ -112,8 +112,8 @@ static inline typelib_TypeClass cpp2uno_call( } else // ptr to complex value | ref { - typelib_TypeDescription * pParamTypeDescr = 0; - TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); + typelib_TypeDescription * pParamTD = NULL; + TYPELIB_DANGER_GET( &pParamTD, rParam.pTypeRef ); void * pCppStack; @@ -122,28 +122,28 @@ static inline typelib_TypeClass cpp2uno_call( if ( !rParam.bIn ) // Pure out { // UNO out is unconstructed mem - pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ); + pUnoArgs[nPos] = alloca( pParamTD->nSize ); pTempIndexes[nTempIndexes] = nPos; - // pParamTypeDescr will be released at reconversion - ppTempParamTypeDescr[nTempIndexes++] = pParamTypeDescr; + // pParamTD will be released at reconversion + ppTempParamTypeDescr[nTempIndexes++] = pParamTD; } // else if ( bridges::cpp_uno::shared::relatesToInterfaceType( - pParamTypeDescr ) ) + pParamTD ) ) { ::uno_copyAndConvertData( - pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ), - pCppStack, pParamTypeDescr, + pUnoArgs[nPos] = alloca( pParamTD->nSize ), + pCppStack, pParamTD, pThis->getBridge()->getCpp2Uno() ); pTempIndexes[nTempIndexes] = nPos; // Has to be reconverted - // pParamTypeDescr will be released at reconversion - ppTempParamTypeDescr[nTempIndexes++] = pParamTypeDescr; + // pParamTD will be released at reconversion + ppTempParamTypeDescr[nTempIndexes++] = pParamTD; } else // direct way { pUnoArgs[nPos] = pCppStack; // No longer needed - TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + TYPELIB_DANGER_RELEASE( pParamTD ); } } } @@ -374,69 +374,23 @@ extern "C" typelib_TypeClass cpp_vtable_call( //================================================================================================== extern "C" { -// From http://msdn.microsoft.com/en-us/library/ssa62fwe%28v=VS.90%29.aspx - -typedef enum _UNWIND_OP_CODES { - UWOP_PUSH_NONVOL = 0, /* info == register number */ - UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ - UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ - UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ - UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ - UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ - UWOP_SAVE_XMM128, /* info == XMM reg number, offset in next slot */ - UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ - UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ -} UNWIND_CODE_OPS; - -typedef union _UNWIND_CODE { - struct { - sal_uChar CodeOffset; - sal_uChar UnwindOp : 4; - sal_uChar OpInfo : 4; - } u; - USHORT FrameOffset; -} UNWIND_CODE, *PUNWIND_CODE; - -#define UNW_FLAG_EHANDLER 0x01 -#define UNW_FLAG_UHANDLER 0x02 -#define UNW_FLAG_CHAININFO 0x04 - -typedef struct _UNWIND_INFO { - sal_uChar Version : 3; - sal_uChar Flags : 5; - sal_uChar SizeOfProlog; - sal_uChar CountOfCodes; - sal_uChar FrameRegister : 4; - sal_uChar FrameOffset : 4; - UNWIND_CODE UnwindCode[1]; -/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1]; -* union { -* OPTIONAL ULONG ExceptionHandler; -* OPTIONAL ULONG FunctionEntry; -* }; -* OPTIONAL ULONG ExceptionData[]; */ -} UNWIND_INFO, *PUNWIND_INFO; - // These are actually addresses in the code compiled from codeSnippet.asm extern char fp_spill_templates, fp_spill_templates_end, trampoline_template, - trampoline_template_spill_end, - trampoline_template_prolog_end, + trampoline_template_function_table, + trampoline_template_spill_params, + trampoline_template_spill_params_end, trampoline_template_function_index, trampoline_template_vtable_offset, + trampoline_template_cpp_vtable_call, trampoline_template_end; } // Just the code -int const codeSnippetCodeSize = - (int) (&trampoline_template_end - &trampoline_template); - -// Including the function table entry and unwind information, plus -// alignment padding int const codeSnippetSize = - codeSnippetCodeSize + (int) (sizeof( RUNTIME_FUNCTION ) + sizeof( UNWIND_INFO )) + 8; + (int) (&trampoline_template_end - &trampoline_template); // This function generates the code that acts as a proxy for the UNO function to be called. // The generated code does the following: @@ -450,7 +404,7 @@ unsigned char * codeSnippet( bool bHasHiddenParam ) { OSL_ASSERT( (&fp_spill_templates_end - &fp_spill_templates) == - (&trampoline_template_spill_end - &trampoline_template) ); + (&trampoline_template_spill_params_end - &trampoline_template_spill_params) ); OSL_ASSERT( ((&fp_spill_templates_end - &fp_spill_templates) / 4) * 4 == (&fp_spill_templates_end - &fp_spill_templates) ); @@ -458,53 +412,47 @@ unsigned char * codeSnippet( if ( bHasHiddenParam ) functionIndex |= 0x80000000; - int const one_spill_instruction_size = (int) ((&fp_spill_templates_end - &fp_spill_templates)) / 4; + int const one_spill_instruction_size = + (int) ((&fp_spill_templates_end - &fp_spill_templates)) / 4; - memcpy( code, &trampoline_template, codeSnippetCodeSize ); + memcpy( code, &trampoline_template, codeSnippetSize ); for (int i = 0; i < 4; ++i) + { if ( param_kind[i] == CPPU_CURRENT_NAMESPACE::REGPARAM_FLT ) - memcpy (code + i*one_spill_instruction_size, + { + memcpy (&trampoline_template_spill_params + i*one_spill_instruction_size, &fp_spill_templates + i*one_spill_instruction_size, one_spill_instruction_size); + } + } - ((sal_uInt64*) trampoline_template_function_index)[-1] = functionIndex; - ((sal_uInt64*) trampoline_template_vtable_offset)[-1] = vtableOffset; + ((sal_uInt64*) (code + (&trampoline_template_function_index + - &trampoline_template)))[-1] = + functionIndex; + ((sal_uInt64*) (code + (&trampoline_template_vtable_offset + - &trampoline_template)))[-1] = + vtableOffset; + ((void**) (code + (&trampoline_template_cpp_vtable_call + - &trampoline_template)))[-1] = + cpp_vtable_call; // Add unwind data for the dynamically generated function by // calling RtlAddFunctionTable(). - // TODO: We need to remove the unwind data with - // RtlDeleteFunctionTable() in freeExec() in - // vtablefactory.cxx. How can we get at the function pointer table - // there? Maybe we should move the function table and unwind info - // to be at the beginning of the allocated block, and add another - // parameter to this function to return the actual trampoline - // start, etc? + // The unwind data is inside the function code, at a fixed offset + // from the function start. See codeSnippet.asm. Actually this is + // unnecessarily complex, we could as well just allocate the + // function table dynamically. But it doesn't hurt either, I + // think. - // Just one function with one unwind info with one unwind code - // included in it. Here we just "know" what the code in - // codeSnippet.asm looks like, sorry. + // The real problem now is that we need to remove the unwind data + // with RtlDeleteFunctionTable() in freeExec() in + // vtablefactory.cxx. See comment there. RUNTIME_FUNCTION *pFunTable = - (RUNTIME_FUNCTION *) (((((sal_uInt64) (code + codeSnippetSize) - 1) / 4) + 1) * 4); - UNWIND_INFO *pUnwInfo = - (UNWIND_INFO *) (pFunTable + 1); - - OSL_ASSERT( (unsigned char *) (pUnwInfo + 1) <= code + codeSnippetSize ); - - pFunTable->BeginAddress = 0; - pFunTable->EndAddress = codeSnippetCodeSize; - pFunTable->UnwindData = (DWORD) ((unsigned char *) pUnwInfo - code); - - pUnwInfo->Version = 1; - pUnwInfo->Flags = 0; - pUnwInfo->SizeOfProlog = (sal_uChar) (&trampoline_template_prolog_end - &trampoline_template); - pUnwInfo->CountOfCodes = 1; - pUnwInfo->FrameRegister = 0; - pUnwInfo->UnwindCode[0].u.CodeOffset = (sal_uChar) (&trampoline_template_prolog_end - &trampoline_template); - pUnwInfo->UnwindCode[0].u.UnwindOp = UWOP_ALLOC_SMALL; - pUnwInfo->UnwindCode[0].u.OpInfo = 4; + (RUNTIME_FUNCTION *) (code + (&trampoline_template_function_table + - &trampoline_template)); RtlAddFunctionTable( pFunTable, 1, (DWORD64) code ); @@ -563,10 +511,10 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( (*slots) -= functionCount; Slot * s = *slots; - for (int i = 0; i < functionCount; ++i) { - typelib_TypeDescription * pTD = 0; + for (int member = 0; member < type->nMembers; ++member) { + typelib_TypeDescription * pTD = NULL; - TYPELIB_DANGER_GET( &pTD, type->ppMembers[ i ] ); + TYPELIB_DANGER_GET( &pTD, type->ppMembers[ member ] ); OSL_ASSERT( pTD ); char param_kind[4]; @@ -606,29 +554,29 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( typelib_InterfaceMethodTypeDescription *pMethodTD = reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( pTD ); - typelib_TypeDescription *pReturnTD = 0; + typelib_TypeDescription *pReturnTD = NULL; TYPELIB_DANGER_GET( &pReturnTD, pMethodTD->pReturnTypeRef ); OSL_ASSERT( pReturnTD ); if ( pReturnTD->nSize > 8 ) { // Hidden return value - nr++; + ++nr; } // 'this' - nr++; + ++nr; - for (int j = 0; nr < 4 && j < pMethodTD->nParams; ++j) + for (int param = 0; nr < 4 && param < pMethodTD->nParams; ++param, ++nr) { - typelib_TypeDescription *pParamTD = 0; + typelib_TypeDescription *pParamTD = NULL; - TYPELIB_DANGER_GET( &pParamTD, pMethodTD->pParams[j].pTypeRef ); + TYPELIB_DANGER_GET( &pParamTD, pMethodTD->pParams[param].pTypeRef ); OSL_ASSERT( pParamTD ); if ( pParamTD->eTypeClass == typelib_TypeClass_FLOAT || pParamTD->eTypeClass == typelib_TypeClass_DOUBLE ) - param_kind[nr++] = CPPU_CURRENT_NAMESPACE::REGPARAM_FLT; + param_kind[nr] = CPPU_CURRENT_NAMESPACE::REGPARAM_FLT; TYPELIB_DANGER_RELEASE( pParamTD ); } diff --git a/bridges/source/cpp_uno/shared/vtablefactory.cxx b/bridges/source/cpp_uno/shared/vtablefactory.cxx index cfc4aa3b5..9fc111bba 100644 --- a/bridges/source/cpp_uno/shared/vtablefactory.cxx +++ b/bridges/source/cpp_uno/shared/vtablefactory.cxx @@ -125,6 +125,14 @@ extern "C" void * SAL_CALL allocExec(rtl_arena_type *, sal_Size * size) { return p; } +#if defined SAL_W32 +extern "C" { +extern char + trampoline_template, + trampoline_template_function_table; +} +#endif + extern "C" void SAL_CALL freeExec( rtl_arena_type *, void * address, sal_Size size) { @@ -133,9 +141,20 @@ extern "C" void SAL_CALL freeExec( #elif defined SAL_W32 (void) size; // unused - // TODO: For wntmscx (x64 Windows), we need to remove the function - // table for the dynamically generated function that was added in - // codeSnippet() in cpp2uno.cxx. + // Remove the function table for the dynamically generated + // function that was added in codeSnippet() in cpp2uno.cxx. + +#if 0 + // This is broken. address is not the address of a code + // snippet. Each virtual memory region alocated with + // VirtualAlloc() contains multiple generated code snippets. We + // need to have a hash table from the base addresses of the region + // to the function tables inside it. + + RUNTIME_FUNCTION *pFunTable = + (RUNTIME_FUNCTION *) ((char *) address + (&trampoline_template_function_table - &trampoline_template)); + RtlDeleteFunctionTable( pFunTable ); +#endif VirtualFree(address, 0, MEM_RELEASE); #elif defined(SAL_OS2) |