[Libreoffice-commits] .: bridges/source

Tor Lillqvist tml at kemper.freedesktop.org
Fri Jan 28 06:41:26 PST 2011


 bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm |   69 ++++--
 bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx     |  160 +++++----------
 bridges/source/cpp_uno/shared/vtablefactory.cxx          |   25 ++
 3 files changed, 124 insertions(+), 130 deletions(-)

New commits:
commit 66787671fa9635f2fb0bd021c21d87b22d007015
Author: Tor Lillqvist <tlillqvist at novell.com>
Date:   Fri Jan 28 16:30:54 2011 +0200

    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.

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 b1a4f73..c02b667 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 615668b..74abccd 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 cfc4aa3..9fc111b 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)


More information about the Libreoffice-commits mailing list