diff options
author | Tor Lillqvist <tlillqvist@novell.com> | 2011-01-27 15:44:09 +0200 |
---|---|---|
committer | Tor Lillqvist <tlillqvist@novell.com> | 2011-01-27 23:39:59 +0200 |
commit | dc433642ab86c55ab1184521f9e7259bed57dab5 (patch) | |
tree | a5b9e9cd1a04d8b20a45e3f12a96e58fdd2532b5 | |
parent | move cxxabi.h after stl headers to workaround gcc 4.6.0 and damn stlport (diff) | |
download | core-dc433642ab86c55ab1184521f9e7259bed57dab5.tar.gz core-dc433642ab86c55ab1184521f9e7259bed57dab5.zip |
More work on the x64 Windows C++-UNO bridge
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_x86-64/asmbits.asm | 328 | ||||
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm | 186 | ||||
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx | 542 | ||||
-rw-r--r-- | bridges/source/cpp_uno/msvc_win32_x86-64/makefile.mk | 11 | ||||
-rw-r--r-- | bridges/source/cpp_uno/shared/vtablefactory.cxx | 5 |
5 files changed, 468 insertions, 604 deletions
diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/asmbits.asm b/bridges/source/cpp_uno/msvc_win32_x86-64/asmbits.asm deleted file mode 100644 index 09f1177b9a9e..000000000000 --- a/bridges/source/cpp_uno/msvc_win32_x86-64/asmbits.asm +++ /dev/null @@ -1,328 +0,0 @@ -; -*- Mode: text; tab-width: 8; indent-tabs-mode: nil comment-column: 32; comment-start: "; " comment-start-skip: "; *" -*- - -;************************************************************************* -;* -;* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -;* -;* Copyright 2000, 2011 Oracle and/or its affiliates. -;* -;* OpenOffice.org - a multi-platform office productivity suite -;* -;* This file is part of OpenOffice.org. -;* -;* OpenOffice.org is free software: you can redistribute it and/or modify -;* it under the terms of the GNU Lesser General Public License version 3 -;* only, as published by the Free Software Foundation. -;* -;* OpenOffice.org is distributed in the hope that it will be useful, -;* but WITHOUT ANY WARRANTY; without even the implied warranty of -;* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;* GNU Lesser General Public License version 3 for more details -;* (a copy is included in the LICENSE file that accompanied this code). -;* -;* You should have received a copy of the GNU Lesser General Public License -;* version 3 along with OpenOffice.org. If not, see -;* <http://www.openoffice.org/license.html> -;* for a copy of the LGPLv3 License. -;* -;************************************************************************ - -; Emacs asm-mode is not really ideal for the convention in this -; file. So I use text-mode with custom comment syntax... not really -; ideal either. Maybe I should just re-format this to match asm-mode's -; conventions? - -; NOTE: EXTREMELY UNFINISHED, I KNOW. WORK IN PROGRESS. - -; I really don't have a good high-level understanding of the big -; picture and what the actual task of the C++/UNO bridge is yet... I -; should debug the x86 version and see what is actually going on to -; get an understanding. - -; This is in a separate file for x86-64 as MSVC doesn't have in-line -; assembly for x64. The code here is still partly just a crude copy of -; the in-line x86 code from ../msvc_win32_intel that is totally -; pointless on x64. But parts have been properly changed into x64 -; calling convention and might even work. - -; Random web links and other documentation about low-level -; implementation details for the C++/UNO bridge on x64 Windows kept -; here: - -; Caolan's "Lazy Hackers Guide To Porting" is useful: -; http://wiki.services.openoffice.org/wiki/Lazy_Hackers_Guide_To_Porting - -; As for details about the x64 Windows calling convention, register -; usage, stack usage, exception handling etc, the official -; documentation (?) on MSDN is a bit fragmented and split up into a -; needlessly large number of short pages. But still: -; http://msdn.microsoft.com/en-us/library/7kcdt6fy%28v=VS.90%29.aspx - -; Also see Raymond Chen's blog post: -; http://blogs.msdn.com/b/oldnewthing/archive/2004/01/14/58579.aspx - -; This one is actually more readable: "Improving Automated Analysis of -; Windows x64 Binaries": -; http://www.uninformed.org/?v=4&a=1 - -; The bridge uses dynamic code generation. For exception handling and -; unwinding to work across that (as I assume we want?), one apparently -; needs to use either RtlAddFunctionTable() or -; RtlInstallFunctionTableCallback(). See Windows SDK documentation. - -; Random interesting discussion threads: -; http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/300bd6d3-9381-4d2d-8129-e48b392c05d8 - -; Ken Johnson's blog http://www.nynaeve.net/ has much interesting -; information, for instance: -; http://www.nynaeve.net/?p=11 - -; From <typelib/typeclass.h> -typelib_TypeClass_VOID = 0 -typelib_TypeClass_CHAR = 1 -typelib_TypeClass_BOOLEAN = 2 -typelib_TypeClass_BYTE = 3 -typelib_TypeClass_SHORT = 4 -typelib_TypeClass_UNSIGNED_SHORT = 5 -typelib_TypeClass_LONG = 6 -typelib_TypeClass_UNSIGNED_LONG = 7 -typelib_TypeClass_HYPER = 8 -typelib_TypeClass_UNSIGNED_HYPER = 9 -typelib_TypeClass_FLOAT = 10 -typelib_TypeClass_DOUBLE = 11 -typelib_TypeClass_ENUM = 15 - -extrn __copyConstruct : proc -extrn __destruct : proc -extrn cpp_mediate: proc - -.code - -; First we have three code snippets that aren't real functions, but -; "naked" (in x86 calling convention parlance) functions jumped to -; from code generated in C++. - -; I think that for x64 Windows we shouldn't be jumping so wildly from -; generated code snippets into fixed code snippets here. "Procedures" -; should be continuous in memory, or the unwinding information will be -; broken. (For dynamically generated code, that is dynamically -; registered unwind information, obviously.) - -; So instead of generating jumps into here, we should just make the -; C++ code copy the machine code that these three "naked" functions -; compile to after the snippet it is generating. Of course, that means -; the code here needs to be position independent, or (eek) that we -; would need to come up with some home-brewed minimal relocation -; mechanism. - -; Jumped to from code generated in except.cxx: ObjectFunction::ObjectFunction() - -copyConstruct proc - ; ObjectFunction this already on stack - push [rsp+8] ; source exc object this - push rcx ; exc object - call __copyConstruct - add rsp, 12 ; + ObjectFunction this - ret 4 -copyConstruct endp - -; Ditto - -destruct proc - ; ObjectFunction this already on stack - push rcx ; exc object - call __destruct - add rsp, 8 ; + ObjectFunction this - ret -destruct endp - -; Jumped to from code generated in cpp2uno.cxx:codeSnippet() - -cpp_vtable_call proc - sub rsp, 8 ; space for immediate return type - push rsp - push rdx ; vtable offset - push rax ; function index - mov rax, rsp - add rax, 20 - push rax ; original stack ptr - - call cpp_mediate - add rsp, 16 - - cmp rax, typelib_TypeClass_FLOAT - je Lfloat - cmp rax, typelib_TypeClass_DOUBLE - je Ldouble - cmp rax, typelib_TypeClass_HYPER - je Lhyper - cmp eax, typelib_TypeClass_UNSIGNED_HYPER - je Lhyper - ; rest is eax - pop rax - add rsp, 4 - ret -Lhyper: - pop rax - pop rdx - ret -Lfloat: - fld dword ptr [rsp] - add rsp, 8 - ret -Ldouble: - fld qword ptr [rsp] - add rsp, 8 - ret -cpp_vtable_call endp - -; Called from uno2cpp.cxx - -; This one is now hopefully proper x64 Windows code following the -; appropriate conventions and might actually work. - -; void callVirtualMethod( ; location on entry -; void * pAdjustedThisPtr, ; rcx -; sal_Int32 nVtableIndex, ; rdx -; void * pReturn, ; r8 -; typelib_TypeClass eReturnTypeClass, ; r9 -; sal_Int64 * pStack, ; ((4+1)*8)[rsp] -; sal_Int32 nStack ; ((5+1)*8)[rsp] -; sal_uInt64 *pGPR, ; ((6+1)*8)[rsp] -; double *pFPR) ; ((7+1)*8)[rsp] - -callVirtualMethod proc frame - ; Prologue - mov ((3+1)*8)[rsp], r9 - mov ((2+1)*8)[rsp], r8 - mov ((0+1)*8)[rsp], rcx - push rsi - .pushreg rsi - push rdi - .pushreg rdi - - ; Our stack frame size is 24 qwords (space for 20 parameters - ; to pass, plus the two pushed registers rsi and rdi, plus - ; return address, rounded up to 16 bytes) - - sub rsp, (20+1)*8 - .allocstack (20+1)*8 - .endprolog - - ; Stack parameters first - - ; nStack is number of qwords of stack space (including spilled - ; registers). If four or less, 4 is passed in anyway to make - ; code here simpler. - - mov rcx, ((24+5)*8)[rsp] ; nStack - sub rcx, 4 - jle Lxmmregs - - mov rsi, ((24+4)*8)[rsp] ; pStack - add rsi, 32 - lea rdi, 32[rsp] - rep movsq - -Lxmmregs: - ; Parameters passed in XMM registers - - ; We don't bother checking which actually needed, if any. - mov rax, ((24+7)*8)[rsp] ; pFPR - movsd xmm0, qword ptr [rax] - movsd xmm1, qword ptr 8[rax] - movsd xmm2, qword ptr 16[rax] - movsd xmm3, qword ptr 24[rax] - - ; Prepare pointer to function to call - mov rcx, ((24+0)*8)[rsp] - mov r12, [rcx] ; pAdjustedThisPtr->vtable - shl rdx, 3 ; nVtableIndex *= 8 - add r12, rdx - - ; Fill parameters passed in general purpose registers - mov rax, ((24+6)*8)[rsp] ; pGPR - mov rcx, (0*8)[rax] - mov rdx, (1*8)[rax] - mov r8, (2*8)[rax] - mov r9, (3*8)[rax] - - call qword ptr [r12] - - ; Test return type - mov r9, ((24+3)*8)[rsp] - cmp r9, typelib_TypeClass_VOID - je Lepilog - - ; int32 - cmp r9, typelib_TypeClass_LONG - je Lint32 - cmp r9, typelib_TypeClass_UNSIGNED_LONG - je Lint32 - cmp r9, typelib_TypeClass_ENUM - je Lint32 - - ; int8 - cmp r9, typelib_TypeClass_BOOLEAN - je Lint8 - cmp r9, typelib_TypeClass_BYTE - je Lint8 - - ; int16 - cmp r9, typelib_TypeClass_CHAR - je Lint16 - cmp r9, typelib_TypeClass_SHORT - je Lint16 - cmp r9, typelib_TypeClass_UNSIGNED_SHORT - je Lint16 - - ; int64 - cmp r9, typelib_TypeClass_HYPER - je Lint64 - cmp r9, typelib_TypeClass_UNSIGNED_HYPER - je Lint64 - - ; float - cmp r9, typelib_TypeClass_FLOAT - je Lfloat - - ; double - cmp r9, typelib_TypeClass_DOUBLE - je Ldouble - - jmp Lepilog ; no simple type - -Lint8: - mov byte ptr [r8], al - jmp Lepilog - -Lint16: - mov word ptr [r8], ax - jmp Lepilog - -Lint32: - mov dword ptr [r8], eax - jmp Lepilog - -Lint64: - mov qword ptr [r8], rax - jmp Lepilog - -Lfloat: - movss dword ptr [r8], xmm0 - jmp Lepilog - -Ldouble: - movsd qword ptr [r8], xmm0 - -Lepilog: - ; Epilogue - add rsp, (20+1)*8 - pop rdi - pop rsi - ret -callVirtualMethod endp - -end - -; vim:set shiftwidth=4 softtabstop=4 expandtab: diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm b/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm new file mode 100644 index 000000000000..30322d350411 --- /dev/null +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/codeSnippet.asm @@ -0,0 +1,186 @@ +; -*- Mode: text; tab-width: 8; indent-tabs-mode: nil comment-column: 44; comment-start: ";; " comment-start-skip: ";; *" -*- + +;; Version: MPL 1.1 / GPLv3+ / LGPLv3+ +;; +;; The contents of this file are subject to the Mozilla Public License Version +;; 1.1 (the "License"); you may not use this file except in compliance with +;; the License or as specified alternatively below. You may obtain a copy of +;; the License at http://www.mozilla.org/MPL/ +;; +;; Software distributed under the License is distributed on an "AS IS" basis, +;; WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +;; for the specific language governing rights and limitations under the +;; License. +;; +;; The Initial Developer of the Original Code is +;; Novell, Inc. +;; Portions created by the Initial Developer are Copyright (C) 2011 the +;; Initial Developer. All Rights Reserved. +;; +;; Major Contributor(s): +;; Tor Lillqvist <tml@iki.fi> +;; Portions created by Tor Lillqvist are Copyright (C) 2011 Tor Lillqvist. All Rights Reserved. +;; +;; For minor contributions see the git repository. +;; +;; Alternatively, the contents of this file may be used under the terms of +;; either the GNU General Public License Version 3 or later (the "GPLv3+"), or +;; the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"), +;; in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable +;; instead of those above. + +;; This is the template source code for the trampoline generated by +;; codeSnippet() in cpp2uno.cxx. codeSnippet() copies the code from +;; this function, modifying it as necessary in a few places. The +;; generated trampoline calls cpp_vtable_call() which then calls the +;; actual UNO function. + +;; We keep this as a separate .asm file here so that it is easy to +;; modify, and we don't need to laborously enter machine code into +;; codeSnippet(). + +;; This is in a separate file for x86-64 as MSVC doesn't have in-line +;; assembly for x64. The code here is still partly just a crude copy of +;; the in-line x86 code from ../msvc_win32_intel that is totally +;; pointless on x64. But parts have been properly changed into x64 +;; calling convention and might even work. + +;; Random web links and other documentation about low-level +;; implementation details for the C++/UNO bridge on x64 Windows kept +;; here: + +;; Caolan's "Lazy Hackers Guide To Porting" is useful: +;; http://wiki.services.openoffice.org/wiki/Lazy_Hackers_Guide_To_Porting + +;; As for details about the x64 Windows calling convention, register +;; usage, stack usage, exception handling etc, the official +;; documentation (?) on MSDN is a bit fragmented and split up into a +;; needlessly large number of short pages. But still: +;; http://msdn.microsoft.com/en-us/library/7kcdt6fy%28v=VS.90%29.aspx + +;; Also see Raymond Chen's blog post: +;; http://blogs.msdn.com/b/oldnewthing/archive/2004/01/14/58579.aspx + +;; This one is actually more readable: "Improving Automated Analysis +;; of Windows x64 Binaries": http://www.uninformed.org/?v=4&a=1 + +;; For exception handling and unwinding to work across the generated +;; functions (as I assume we want?), we would need call +;; RtlAddFunctionTable() (and RtlDeleteFunctionTable()). See Windows +;; SDK documentation. + +;; Random interesting discussion threads: +;; http://social.msdn.microsoft.com/Forums/en/vcgeneral/thread/300bd6d3-9381-4d2d-8129-e48b392c05d8 + +;; Ken Johnson's blog http://www.nynaeve.net/ has much interesting +;; information, for instance: +;; http://www.nynaeve.net/?p=11 + +;; The code snippet generated is called from "normal" C++ code which +;; has no idea that it is calling dynamically generated code. + +typelib_TypeClass_FLOAT equ 10 +typelib_TypeClass_DOUBLE equ 11 + +extern cpp_vtable_call: proc + +.code + +;; Single instruction templates. For each register paramter, which can +;; be either in an integer or floating-point register, either of two +;; instruction sequences are used, either: +;; mov qword ptr offset[rsp], reg +;; nop +;; or: +;; movsd qwort ptr offset[rsp], xmmreg +;; +;; The nop in the integer case is so that both are of equal length + +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 +fp_spill_templates_end: +public fp_spill_templates_end + +;; The actual template function code here + +trampoline_template proc + + ;; Spill our register parameters. In the x64 Windows calling + ;; convention the caller always has stack space allocated + ;; where the callee can spill register parameters. + + ;; The default is integer moves, that are replaced in the + ;; generated code snippet with floating-point moves for + ;; floating-point parameters. + + mov qword ptr 32[rsp], r9 + nop + mov qword ptr 24[rsp], r8 + nop + mov qword ptr 16[rsp], rdx + nop + mov qword ptr 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 + + ;; Call cpp_vtable_call() with 3 parameters: + + ;; 1 (rcx): nFunctionIndex + ;; 2 (rdx): nVtableOffset + ;; 3 (r8): pointer to where to store return value, followed by our + ;; return address (uninteresting to cpp_vtable_call()), followed + ;; by our spilled register parameters, as stored above, followed + ;; by the rest of our parameters, if any. + + mov rcx, 12345467890abcdeh ;; nFunctionIndex, actual value generated from + ;; parameter to codeSnippet() +trampoline_template_function_index:: +public trampoline_template_function_index + + mov rdx, 12345467890abcdeh ;; nVtableOffset, ditto +trampoline_template_vtable_offset:: +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() + + ;; cpp_vtable_call() returns the typelib_TypeClass type of the + ;; return value of the called UNO function + + cmp rax, typelib_TypeClass_FLOAT + je Lfloat + + cmp rax, typelib_TypeClass_DOUBLE + je Lfloat + + mov rax, qword ptr 32[rsp] + jmp Lepilogue + +Lfloat: + movsd xmm0, qword ptr 32[rsp] + +Lepilogue: + add rsp, 40 + ret +trampoline_template_end:: +public trampoline_template_end + +trampoline_template endp + +end + +; vim:set shiftwidth=4 softtabstop=4 expandtab: 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 a2310cbd8d7d..29545337b16b 100644 --- a/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/cpp2uno.cxx @@ -42,191 +42,180 @@ #include "mscx.hxx" -using namespace ::com::sun::star::uno; +// #define DEBUG_WITH_JUST_MESSAGEBOXES -namespace -{ +using namespace ::com::sun::star::uno; //================================================================================================== static inline typelib_TypeClass cpp2uno_call( bridges::cpp_uno::shared::CppInterfaceProxy * pThis, const typelib_TypeDescription * pMemberTypeDescr, - typelib_TypeDescriptionReference * pReturnTypeRef, // 0 indicates void return + typelib_TypeDescriptionReference * pReturnTypeRef, // NULL indicates void return sal_Int32 nParams, typelib_MethodParameter * pParams, - void ** pCallStack, - sal_Int64 * pRegisterReturn /* space for register return */ ) + void ** pStack ) { - // pCallStack: ret, this, [complex return ptr], params - char * pCppStack = (char *)(pCallStack +2); - - // return - typelib_TypeDescription * pReturnTypeDescr = 0; - if (pReturnTypeRef) - { + // Return type + typelib_TypeDescription * pReturnTypeDescr = NULL; + if ( pReturnTypeRef ) TYPELIB_DANGER_GET( &pReturnTypeDescr, pReturnTypeRef ); - } - void * pUnoReturn = 0; - void * pCppReturn = 0; // complex return ptr: if != 0 && != pUnoReturn, reconversion need + int nFirstRealParam = 2; - if (pReturnTypeDescr) + void * pUnoReturn = NULL; + void * pCppReturn = NULL; // Complex return ptr: if != NULL && != pUnoReturn, reconversion need + + if ( pReturnTypeDescr ) { - if (bridges::cpp_uno::shared::isSimpleType( pReturnTypeDescr )) + if ( !bridges::cpp_uno::shared::isSimpleType( pReturnTypeDescr ) ) { - pUnoReturn = pRegisterReturn; // direct way for simple types + pCppReturn = pStack[nFirstRealParam++]; + + pUnoReturn = ( bridges::cpp_uno::shared::relatesToInterfaceType( pReturnTypeDescr ) + ? alloca( pReturnTypeDescr->nSize ) + : pCppReturn ); // direct way } - else // complex return via ptr (pCppReturn) + else // complex return, store it directly where the C++ called wants it { - pCppReturn = *(void **)pCppStack; - pCppStack += sizeof(void *); - - pUnoReturn = (bridges::cpp_uno::shared::relatesToInterfaceType( - pReturnTypeDescr ) - ? alloca( pReturnTypeDescr->nSize ) - : pCppReturn); // direct way + pUnoReturn = pStack; } } - // parameters - void ** pUnoArgs = (void **)alloca( 4 * sizeof(void *) * nParams ); - void ** pCppArgs = pUnoArgs + nParams; - // indexes of values this have to be converted (interface conversion cpp<=>uno) - sal_Int64 * pTempIndexes = (sal_Int64 *)(pUnoArgs + (2 * nParams)); - // type descriptions for reconversions - typelib_TypeDescription ** ppTempParamTypeDescr = (typelib_TypeDescription **)(pUnoArgs + (3 * nParams)); + void ** pCppIncomingParams = pStack + nFirstRealParam; + + // Unlike this method for other archs, prefer clarity to + // micro-optimization, and allocate these array separately + + // Parameters passed to the UNO function + void ** pUnoArgs = (void **)alloca( sizeof(void *) * nParams ); - sal_Int32 nTempIndexes = 0; + // Parameters received from C++ + void ** pCppArgs = (void **)alloca( sizeof(void *) * nParams ); - for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + // Indexes of values this have to be converted (interface conversion cpp<=>uno) + int * pTempIndexes = + (int *)alloca( sizeof(int) * nParams ); + + // Type descriptions for reconversions + typelib_TypeDescription ** ppTempParamTypeDescr = + (typelib_TypeDescription **)alloca( sizeof(void *) * nParams ); + + int nTempIndexes = 0; + + for ( int nPos = 0; nPos < nParams; ++nPos ) { const typelib_MethodParameter & rParam = pParams[nPos]; - typelib_TypeDescription * pParamTypeDescr = 0; - TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); - if (!rParam.bOut - && bridges::cpp_uno::shared::isSimpleType( pParamTypeDescr )) - // value + if ( !rParam.bOut + && bridges::cpp_uno::shared::isSimpleType( rParam.pTypeRef ) ) { - pCppArgs[nPos] = pCppStack; - pUnoArgs[nPos] = pCppStack; - switch (pParamTypeDescr->eTypeClass) - { - case typelib_TypeClass_HYPER: - case typelib_TypeClass_UNSIGNED_HYPER: - case typelib_TypeClass_DOUBLE: - pCppStack += sizeof(sal_Int64); // extra qword - break; - default: - break; - } - // no longer needed - TYPELIB_DANGER_RELEASE( pParamTypeDescr ); + pCppArgs[nPos] = pUnoArgs[nPos] = pCppIncomingParams++; } else // ptr to complex value | ref { - pCppArgs[nPos] = *(void **)pCppStack; + typelib_TypeDescription * pParamTypeDescr = 0; + TYPELIB_DANGER_GET( &pParamTypeDescr, rParam.pTypeRef ); - if (! rParam.bIn) // is pure out + void * pCppStack; + + pCppArgs[nPos] = pCppStack = *pCppIncomingParams++; + + if ( !rParam.bIn ) // Pure out { - // uno out is unconstructed mem! + // UNO out is unconstructed mem pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ); pTempIndexes[nTempIndexes] = nPos; - // will be released at reconversion + // pParamTypeDescr will be released at reconversion ppTempParamTypeDescr[nTempIndexes++] = pParamTypeDescr; } - // is in/inout - else if (bridges::cpp_uno::shared::relatesToInterfaceType( - pParamTypeDescr )) + // + else if ( bridges::cpp_uno::shared::relatesToInterfaceType( + pParamTypeDescr ) ) { ::uno_copyAndConvertData( pUnoArgs[nPos] = alloca( pParamTypeDescr->nSize ), - *(void **)pCppStack, pParamTypeDescr, + pCppStack, pParamTypeDescr, pThis->getBridge()->getCpp2Uno() ); - pTempIndexes[nTempIndexes] = nPos; // has to be reconverted - // will be released at reconversion + pTempIndexes[nTempIndexes] = nPos; // Has to be reconverted + // pParamTypeDescr will be released at reconversion ppTempParamTypeDescr[nTempIndexes++] = pParamTypeDescr; } else // direct way { - pUnoArgs[nPos] = *(void **)pCppStack; - // no longer needed + pUnoArgs[nPos] = pCppStack; + // No longer needed TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } } - pCppStack += sizeof(sal_Int64); // standard parameter length } // ExceptionHolder uno_Any aUnoExc; // Any will be constructed by callee uno_Any * pUnoExc = &aUnoExc; - // invoke uno dispatch call + // invoke UNO dispatch call (*pThis->getUnoI()->pDispatcher)( pThis->getUnoI(), pMemberTypeDescr, pUnoReturn, pUnoArgs, &pUnoExc ); // in case an exception occurred... - if (pUnoExc) + if ( pUnoExc ) { - // destruct temporary in/inout params - while (nTempIndexes--) + // Destruct temporary in/inout params + while ( nTempIndexes-- ) { - sal_Int32 nIndex = pTempIndexes[nTempIndexes]; + int nIndex = pTempIndexes[nTempIndexes]; - if (pParams[nIndex].bIn) // is in/inout => was constructed + if ( pParams[nIndex].bIn ) // Is in/inout => was constructed { ::uno_destructData( pUnoArgs[nIndex], ppTempParamTypeDescr[nTempIndexes], 0 ); } TYPELIB_DANGER_RELEASE( ppTempParamTypeDescr[nTempIndexes] ); } - if (pReturnTypeDescr) - { + if ( pReturnTypeDescr ) TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); - } CPPU_CURRENT_NAMESPACE::mscx_raiseException( - &aUnoExc, pThis->getBridge()->getUno2Cpp() ); - // has to destruct the any - // is here for dummy + &aUnoExc, pThis->getBridge()->getUno2Cpp() ); // Has to destruct the any + + // Is here for dummy return typelib_TypeClass_VOID; } - else // else no exception occurred... + else // Else, no exception occurred... { - // temporary params + // Temporary params while (nTempIndexes--) { - sal_Int32 nIndex = pTempIndexes[nTempIndexes]; + int nIndex = pTempIndexes[nTempIndexes]; typelib_TypeDescription * pParamTypeDescr = ppTempParamTypeDescr[nTempIndexes]; - if (pParams[nIndex].bOut) // inout/out + if ( pParams[nIndex].bOut ) // inout/out { - // convert and assign + // Convert and assign ::uno_destructData( pCppArgs[nIndex], pParamTypeDescr, cpp_release ); ::uno_copyAndConvertData( pCppArgs[nIndex], pUnoArgs[nIndex], pParamTypeDescr, pThis->getBridge()->getUno2Cpp() ); } - // destroy temp uno param + // Destroy temp uno param ::uno_destructData( pUnoArgs[nIndex], pParamTypeDescr, 0 ); TYPELIB_DANGER_RELEASE( pParamTypeDescr ); } - // return - if (pCppReturn) // has complex return + // Return + if ( pCppReturn ) // Has complex return { - if (pUnoReturn != pCppReturn) // needs reconversion + if ( pUnoReturn != pCppReturn ) // Needs reconversion { ::uno_copyAndConvertData( pCppReturn, pUnoReturn, pReturnTypeDescr, pThis->getBridge()->getUno2Cpp() ); - // destroy temp uno return - ::uno_destructData( - pUnoReturn, pReturnTypeDescr, 0 ); + // Destroy temp uno return + ::uno_destructData( pUnoReturn, pReturnTypeDescr, 0 ); } - // complex return ptr is set to eax - *(void **)pRegisterReturn = pCppReturn; + // Complex return ptr is set to eax + pStack[0] = pCppReturn; } - if (pReturnTypeDescr) + if ( pReturnTypeDescr ) { typelib_TypeClass eRet = (typelib_TypeClass)pReturnTypeDescr->eTypeClass; TYPELIB_DANGER_RELEASE( pReturnTypeDescr ); @@ -238,199 +227,204 @@ static inline typelib_TypeClass cpp2uno_call( } //================================================================================================== -typelib_TypeClass __cdecl cpp_mediate( - void ** pCallStack, sal_Int32 nFunctionIndex, sal_Int32 nVtableOffset, - sal_Int64 * pRegisterReturn /* space for register return */ ) +extern "C" typelib_TypeClass cpp_vtable_call( + sal_Int32 nFunctionIndex, + sal_Int32 nVtableOffset, + void ** pStack ) { - // pCallStack: ret adr, this, [ret *], params - void * pThis = static_cast< char * >(pCallStack[1]) - nVtableOffset; - bridges::cpp_uno::shared::CppInterfaceProxy * pCppI - = bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( - pThis); + // pStack points to space for return value, after which + // follows our return address (uninteresting) then the spilled + // integer or floating-point register parameters from the call to + // the trampoline, followed by stack parameters. Note that if the + // callee returns a large value, the first parameter is actually a + // pointer to where it should store its return value. The first + // "real" parameter is the "this" pointer. + + void * pThis; + if ( nFunctionIndex & 0x80000000 ) + { + nFunctionIndex &= 0x7fffffff; + pThis = pStack[3]; + } + else + { + pThis = pStack[2]; + } + pThis = static_cast<char *>( pThis ) - nVtableOffset; + + bridges::cpp_uno::shared::CppInterfaceProxy * pCppI = + bridges::cpp_uno::shared::CppInterfaceProxy::castInterfaceToProxy( pThis ); typelib_InterfaceTypeDescription * pTypeDescr = pCppI->getTypeDescr(); - OSL_ENSURE( nFunctionIndex < pTypeDescr->nMapFunctionIndexToMemberIndex, - "### illegal vtable index!" ); - if (nFunctionIndex >= pTypeDescr->nMapFunctionIndexToMemberIndex) + + OSL_ENSURE( nFunctionIndex < pTypeDescr->nMapFunctionIndexToMemberIndex, "### illegal vtable index!\n" ); + if ( nFunctionIndex >= pTypeDescr->nMapFunctionIndexToMemberIndex ) { - throw RuntimeException( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("illegal vtable index!") ), - (XInterface *)pThis ); + throw RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("illegal vtable index!")), + reinterpret_cast<XInterface *>( pCppI ) ); } // determine called method sal_Int32 nMemberPos = pTypeDescr->pMapFunctionIndexToMemberIndex[nFunctionIndex]; - OSL_ENSURE( nMemberPos < pTypeDescr->nAllMembers, "### illegal member index!" ); + OSL_ENSURE( nMemberPos < pTypeDescr->nAllMembers, "### illegal member index!\n" ); TypeDescription aMemberDescr( pTypeDescr->ppAllMembers[nMemberPos] ); typelib_TypeClass eRet; - switch (aMemberDescr.get()->eTypeClass) - { - case typelib_TypeClass_INTERFACE_ATTRIBUTE: - { - if (pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex) - { - // is GET method - eRet = cpp2uno_call( - pCppI, aMemberDescr.get(), - ((typelib_InterfaceAttributeTypeDescription *)aMemberDescr.get())->pAttributeTypeRef, - 0, 0, // no params - pCallStack, pRegisterReturn ); - } - else - { - // is SET method - typelib_MethodParameter aParam; - aParam.pTypeRef = - ((typelib_InterfaceAttributeTypeDescription *)aMemberDescr.get())->pAttributeTypeRef; - aParam.bIn = sal_True; - aParam.bOut = sal_False; - - eRet = cpp2uno_call( - pCppI, aMemberDescr.get(), - 0, // indicates void return - 1, &aParam, - pCallStack, pRegisterReturn ); - } - break; - } - case typelib_TypeClass_INTERFACE_METHOD: + switch ( aMemberDescr.get()->eTypeClass ) { - // is METHOD - switch (nFunctionIndex) + case typelib_TypeClass_INTERFACE_ATTRIBUTE: { - // standard XInterface vtable calls - case 1: // acquire() - pCppI->acquireProxy(); // non virtual call! - eRet = typelib_TypeClass_VOID; - break; - case 2: // release() - pCppI->releaseProxy(); // non virtual call! - eRet = typelib_TypeClass_VOID; + typelib_TypeDescriptionReference *pAttrTypeRef = + reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( aMemberDescr.get() )->pAttributeTypeRef; + + if ( pTypeDescr->pMapMemberIndexToFunctionIndex[nMemberPos] == nFunctionIndex ) + { + // is GET method + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), pAttrTypeRef, + 0, NULL, // no params + pStack ); + } + else + { + // is SET method + typelib_MethodParameter aParam; + aParam.pTypeRef = pAttrTypeRef; + aParam.bIn = sal_True; + aParam.bOut = sal_False; + + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), + 0, // indicates void return + 1, &aParam, + pStack ); + } break; - case 0: // queryInterface() opt + } + case typelib_TypeClass_INTERFACE_METHOD: { - typelib_TypeDescription * pTD = 0; - TYPELIB_DANGER_GET( &pTD, reinterpret_cast< Type * >( pCallStack[3] )->getTypeLibType() ); - if (pTD) + // is METHOD + switch ( nFunctionIndex ) { - XInterface * pInterface = 0; - (*pCppI->getBridge()->getCppEnv()->getRegisteredInterface)( - pCppI->getBridge()->getCppEnv(), - (void **)&pInterface, pCppI->getOid().pData, - (typelib_InterfaceTypeDescription *)pTD ); - - if (pInterface) - { - ::uno_any_construct( - reinterpret_cast< uno_Any * >( pCallStack[2] ), - &pInterface, pTD, cpp_acquire ); - pInterface->release(); - TYPELIB_DANGER_RELEASE( pTD ); - *(void **)pRegisterReturn = pCallStack[2]; - eRet = typelib_TypeClass_ANY; + case 1: // acquire() + pCppI->acquireProxy(); // non virtual call! + eRet = typelib_TypeClass_VOID; + break; + case 2: // release() + pCppI->releaseProxy(); // non virtual call! + eRet = typelib_TypeClass_VOID; break; + case 0: // queryInterface() opt + { + typelib_TypeDescription * pTD = NULL; + // the incoming C++ parameters are: The hidden return value pointer, + // the this pointer, and then the actual queryInterface() only parameter. + // Thus pStack[4], the third parameter. + TYPELIB_DANGER_GET( &pTD, reinterpret_cast<Type *>( pStack[4] )->getTypeLibType() ); + + if ( pTD ) + { + XInterface * pInterface = NULL; + (*pCppI->getBridge()->getCppEnv()->getRegisteredInterface) + ( pCppI->getBridge()->getCppEnv(), + (void **)&pInterface, + pCppI->getOid().pData, + reinterpret_cast<typelib_InterfaceTypeDescription *>( pTD ) ); + + if ( pInterface ) + { + // pStack[0] = hidden return value pointer + ::uno_any_construct( reinterpret_cast<uno_Any *>( pStack[0] ), + &pInterface, pTD, cpp_acquire ); + + pInterface->release(); + TYPELIB_DANGER_RELEASE( pTD ); + + eRet = typelib_TypeClass_ANY; + break; + } + TYPELIB_DANGER_RELEASE( pTD ); + } + } // Fall through! + default: + { + typelib_InterfaceMethodTypeDescription *pMethodTD = + reinterpret_cast<typelib_InterfaceMethodTypeDescription *>( aMemberDescr.get() ); + + eRet = cpp2uno_call( pCppI, aMemberDescr.get(), + pMethodTD->pReturnTypeRef, + pMethodTD->nParams, + pMethodTD->pParams, + pStack ); } - TYPELIB_DANGER_RELEASE( pTD ); } - } // else perform queryInterface() + break; + } default: - eRet = cpp2uno_call( - pCppI, aMemberDescr.get(), - ((typelib_InterfaceMethodTypeDescription *)aMemberDescr.get())->pReturnTypeRef, - ((typelib_InterfaceMethodTypeDescription *)aMemberDescr.get())->nParams, - ((typelib_InterfaceMethodTypeDescription *)aMemberDescr.get())->pParams, - pCallStack, pRegisterReturn ); + { + throw RuntimeException( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("no member description found!")), + reinterpret_cast<XInterface *>( pCppI ) ); + // is here for dummy + eRet = typelib_TypeClass_VOID; } - break; - } - default: - { - throw RuntimeException( - rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("no member description found!") ), - (XInterface *)pThis ); - // is here for dummy - eRet = typelib_TypeClass_VOID; - } } return eRet; } -extern void *cpp_vtable_call; - //================================================================================================== -int const codeSnippetSize = 28; +extern "C" { + +// 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_function_index, + trampoline_template_vtable_offset, + trampoline_template_end; +} + +int const codeSnippetSize = (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: // - Save register parametrs. unsigned char * codeSnippet( - unsigned char * code, sal_Int32 functionIndex, sal_Int32 vtableOffset) + unsigned char * code, + char param_kind[4], + sal_Int32 functionIndex, + sal_Int32 vtableOffset, + bool bHasHiddenParam ) { - unsigned char * p = code; - - // mov eax, functionIndex: - *p++ = 0xB8; - *reinterpret_cast< sal_Int32 * >(p) = functionIndex; - p += sizeof (sal_Int32); - - // mov edx, vtableOffset: - *p++ = 0xBA; - *reinterpret_cast< sal_Int32 * >(p) = vtableOffset; - p += sizeof (sal_Int32); - -#if 0 - sub esp, 8 // space for immediate return type - push esp - push edx // vtable offset - push eax // function index - mov eax, esp - add eax, 20 - push eax // original stack ptr - - call cpp_mediate - add esp, 16 - - cmp eax, typelib_TypeClass_FLOAT - je Lfloat - cmp eax, typelib_TypeClass_DOUBLE - je Ldouble - cmp eax, typelib_TypeClass_HYPER - je Lhyper - cmp eax, typelib_TypeClass_UNSIGNED_HYPER - je Lhyper - // rest is eax - pop eax - add esp, 4 - ret -Lhyper: - pop eax - pop edx - ret -Lfloat: - fld dword ptr [esp] - add esp, 8 - ret -Ldouble: - fld qword ptr [esp] - add esp, 8 - ret + OSL_ASSERT( (&fp_spill_templates_end - &fp_spill_templates) == + (&trampoline_template_spill_end - &trampoline_template) ); -#endif + OSL_ASSERT( ((&fp_spill_templates_end - &fp_spill_templates) / 4) * 4 == + (&fp_spill_templates_end - &fp_spill_templates) ); -#if 0 - // jmp rel64 cpp_vtable_call: - *p++ = 0xE9; - *reinterpret_cast< sal_Int64 * >(p) - = ((unsigned char *) cpp_vtable_call) - p - sizeof (sal_Int64); - p += sizeof (sal_Int64); -#endif - OSL_ASSERT(p - code <= codeSnippetSize); - return code + codeSnippetSize; -} + if ( bHasHiddenParam ) + functionIndex |= 0x80000000; + int const one_spill_instruction_size = (int) ((&fp_spill_templates_end - &fp_spill_templates)) / 4; + + 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, + &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; + + // TODO: Add unwind data for the dynamically generated function by + // calling RtlAddFunctionTable(). We also need to remove the + // unwind data with RtlDeleteFunctionTable() in freeExec() then. + + return code + codeSnippetSize; } struct bridges::cpp_uno::shared::VtableFactory::Slot { void * fn; }; @@ -468,9 +462,7 @@ bridges::cpp_uno::shared::VtableFactory::initializeBlock( return slots + slotCount; } -#if 0 - -#else +#ifdef DEBUG_WITH_JUST_MESSAGEBOXES static void whatthefuck(sal_Int64 i, ...) { @@ -493,28 +485,33 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( TYPELIB_DANGER_GET( &pTD, type->ppMembers[ i ] ); OSL_ASSERT( pTD ); - CPPU_CURRENT_NAMESPACE::RegParamKind param_kind[4]; + char param_kind[4]; int nr = 0; + for (int i = 0; i < 4; ++i) + param_kind[i] = CPPU_CURRENT_NAMESPACE::REGPARAM_INT; + if ( pTD->eTypeClass == typelib_TypeClass_INTERFACE_ATTRIBUTE ) { typelib_InterfaceAttributeTypeDescription *pAttrTD = reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>( pTD ); - // get method -#if 0 + // Getter + +#ifndef DEBUG_WITH_JUST_MESSAGEBOXES (s++)->fn = code; - code = codeSnippet(code, functionOffset++, vtableOffset); + code = codeSnippet( code, param_kind, functionOffset++, vtableOffset, + pTD->nSize > 8); #else (s++)->fn = whatthefuck; #endif if ( ! pAttrTD->bReadOnly ) { - // set method -#if 0 + // Setter +#ifndef DEBUG_WITH_JUST_MESSAGEBOXES (s++)->fn = code; - code = codeSnippet(code, functionOffset++, vtableOffset); + code = codeSnippet( code, param_kind, functionOffset++, vtableOffset, false ); #else (s++)->fn = whatthefuck; #endif @@ -529,14 +526,14 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( TYPELIB_DANGER_GET( &pReturnTD, pMethodTD->pReturnTypeRef ); OSL_ASSERT( pReturnTD ); - if ( pReturnTD->nSize > 8 ) { + if ( pReturnTD->nSize > 8 ) + { // Hidden return value - param_kind[nr++] = CPPU_CURRENT_NAMESPACE::REGPARAM_INT; + nr++; } - TYPELIB_DANGER_RELEASE( pReturnTD ); // 'this' - param_kind[nr++] = CPPU_CURRENT_NAMESPACE::REGPARAM_INT; + nr++; for (int j = 0; nr < 4 && j < pMethodTD->nParams; ++j) { @@ -548,21 +545,24 @@ unsigned char * bridges::cpp_uno::shared::VtableFactory::addLocalFunctions( if ( pParamTD->eTypeClass == typelib_TypeClass_FLOAT || pParamTD->eTypeClass == typelib_TypeClass_DOUBLE ) param_kind[nr++] = CPPU_CURRENT_NAMESPACE::REGPARAM_FLT; - else - param_kind[nr++] = CPPU_CURRENT_NAMESPACE::REGPARAM_INT; TYPELIB_DANGER_RELEASE( pParamTD ); + } -#if 0 - (s++)->fn = code; - code = codeSnippet(code, functionOffset++, vtableOffset); +#ifndef DEBUG_WITH_JUST_MESSAGEBOXES + (s++)->fn = code; + code = codeSnippet( code, param_kind, functionOffset++, vtableOffset, + pReturnTD->nSize > 8); #else - (s++)->fn = whatthefuck; + (s++)->fn = whatthefuck; #endif - } + + TYPELIB_DANGER_RELEASE( pReturnTD ); } else OSL_ASSERT( false ); + + TYPELIB_DANGER_RELEASE( pTD ); } return code; } diff --git a/bridges/source/cpp_uno/msvc_win32_x86-64/makefile.mk b/bridges/source/cpp_uno/msvc_win32_x86-64/makefile.mk index 604799a18e5a..e813fad0c4cc 100644 --- a/bridges/source/cpp_uno/msvc_win32_x86-64/makefile.mk +++ b/bridges/source/cpp_uno/msvc_win32_x86-64/makefile.mk @@ -51,10 +51,11 @@ CFLAGS += -DLEAK_STATIC_DATA SLOFILES= \ - $(SLO)$/cpp2uno.obj \ - $(SLO)$/uno2cpp.obj \ - $(SLO)$/dllinit.obj \ - $(SLO)$/except.obj + $(SLO)$/cpp2uno.obj \ + $(SLO)$/uno2cpp.obj \ + $(SLO)$/dllinit.obj \ + $(SLO)$/except.obj \ + $(SLO)$/codeSnippet.obj NOOPTFILES= \ $(SLO)$/except.obj @@ -70,7 +71,7 @@ SHL1OBJS = $(SLOFILES) SHL1LIBS = $(SLB)$/cpp_uno_shared.lib SHL1STDLIBS= \ - $(CPPULIB) \ + $(CPPULIB) \ $(SALLIB) DEF1NAME=$(SHL1TARGET) diff --git a/bridges/source/cpp_uno/shared/vtablefactory.cxx b/bridges/source/cpp_uno/shared/vtablefactory.cxx index 4b819632c863..eeeeedf69969 100644 --- a/bridges/source/cpp_uno/shared/vtablefactory.cxx +++ b/bridges/source/cpp_uno/shared/vtablefactory.cxx @@ -132,6 +132,11 @@ extern "C" void SAL_CALL freeExec( munmap(static_cast< char * >(address), size); #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. + VirtualFree(address, 0, MEM_RELEASE); #elif defined(SAL_OS2) (void) DosFreeMem( address); |