[PATCH 4/4] oleaut32: handle by-val to by-reference passing magic on x86_64

Marcus Meissner marcus at jet.franken.de
Sun Nov 5 09:45:18 CST 2017


The Win64 API does not pass structures on the stack, but implicit
by reference. Handle this during the generated stub/proxy calls.

Also changed argsize to always return 1 on x86_64 due to this.

Fixes https://bugs.winehq.org/show_bug.cgi?id=26768

Signed-off-by: Marcus Meissner <marcus at jet.franken.de>
---
 dlls/oleaut32/tmarshal.c | 103 ++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 84 insertions(+), 19 deletions(-)

diff --git a/dlls/oleaut32/tmarshal.c b/dlls/oleaut32/tmarshal.c
index e71e9771f4..f269822f08 100644
--- a/dlls/oleaut32/tmarshal.c
+++ b/dlls/oleaut32/tmarshal.c
@@ -135,16 +135,16 @@ _unmarshal_interface(marshal_state *buf, REFIID riid, LPUNKNOWN *pUnk) {
     DWORD		xsize;
 
     TRACE("...%s...\n",debugstr_guid(riid));
-    
+
     *pUnk = NULL;
     hres = xbuf_get(buf,(LPBYTE)&xsize,sizeof(xsize));
     if (hres) {
         ERR("xbuf_get failed\n");
         return hres;
     }
-    
+
     if (xsize == 0) return S_OK;
-    
+
     hres = CreateStreamOnHGlobal(0,TRUE,&pStm);
     if (hres) {
 	ERR("Stream create failed %x\n",hres);
@@ -659,8 +659,10 @@ static const IRpcProxyBufferVtbl tmproxyvtable = {
 
 /* how much space do we use on stack in DWORD_PTR steps. */
 static int
-_argsize(TYPEDESC *tdesc, ITypeInfo *tinfo) {
+_argsize(TYPEDESC *tdesc, ITypeInfo *tinfo, int *byref) {
     DWORD ret;
+
+    *byref = 0;
     switch (tdesc->vt) {
     case VT_I8:
     case VT_UI8:
@@ -677,9 +679,11 @@ _argsize(TYPEDESC *tdesc, ITypeInfo *tinfo) {
         break;
     case VT_DECIMAL:
         ret = sizeof(DECIMAL);
+	*byref = 1;
         break;
     case VT_VARIANT:
         ret = sizeof(VARIANT);
+	*byref = 1;
         break;
     case VT_USERDEFINED:
     {
@@ -687,6 +691,7 @@ _argsize(TYPEDESC *tdesc, ITypeInfo *tinfo) {
         TYPEATTR *tattr;
         HRESULT hres;
 
+	*byref = 1;
         hres = ITypeInfo_GetRefTypeInfo(tinfo,tdesc->u.hreftype,&tinfo2);
         if (FAILED(hres))
             return 0; /* should fail critically in serialize_param */
@@ -701,6 +706,12 @@ _argsize(TYPEDESC *tdesc, ITypeInfo *tinfo) {
         break;
     }
 
+#ifdef __x86_64__
+    /* On x86_64 we do not store byvalue things on the stack, everything
+     * complex or larger than a register will be spilled and passed by reference */
+    if (*byref)
+        return 1;
+#endif
     return (ret + sizeof(DWORD_PTR) - 1) / sizeof(DWORD_PTR);
 }
 
@@ -749,6 +760,9 @@ _xsize(const TYPEDESC *td, ITypeInfo *tinfo) {
         ITypeInfo_Release(tinfo2);
         return ret;
     }
+    case VT_UINT:
+    case VT_INT:
+        return 4;
     default:
 	return sizeof(DWORD_PTR);
     }
@@ -888,6 +902,7 @@ serialize_param(
                 }
                 break;
 	    case TKIND_ENUM:	/* confirmed */
+		break;
 	    case TKIND_RECORD:	/* FIXME: mostly untested */
 		break;
 	    case TKIND_DISPATCH:	/* will be done in VT_USERDEFINED case */
@@ -1458,7 +1473,7 @@ static DWORD WINAPI xCall(int method, void **args)
     UINT		nrofnames;
     DWORD		remoteresult = 0;
     ITypeInfo 		*tinfo;
-    IRpcChannelBuffer *chanbuf;
+    IRpcChannelBuffer	*chanbuf;
 
     EnterCriticalSection(&tpinfo->crit);
 
@@ -1509,17 +1524,21 @@ static DWORD WINAPI xCall(int method, void **args)
     xargs = (DWORD_PTR *)(args + 1);
     for (i=0;i<fdesc->cParams;i++) {
 	ELEMDESC	*elem = fdesc->lprgelemdescParam+i;
+	DWORD_PTR	*argsptr;
+	int		byref, argsize;
+
 	if (TRACE_ON(olerelay)) {
 	    if (i) TRACE_(olerelay)(",");
 	    if (i+1<nrofnames && names[i+1])
 		TRACE_(olerelay)("%s=",relaystr(names[i+1]));
 	}
+        argsize = _argsize(&elem->tdesc, tinfo, &byref);
 	/* No need to marshal other data than FIN and any VT_PTR. */
         if (!is_in_elem(elem))
         {
             if (elem->tdesc.vt != VT_PTR)
             {
-                xargs+=_argsize(&elem->tdesc, tinfo);
+                xargs += argsize;
                 TRACE_(olerelay)("[out]");
                 continue;
             }
@@ -1529,13 +1548,17 @@ static DWORD WINAPI xCall(int method, void **args)
             }
         }
 
+	argsptr = xargs;
+	if (byref)
+	    argsptr = *(DWORD_PTR**)xargs;
+
 	hres = serialize_param(
 	    tinfo,
 	    is_in_elem(elem),
 	    TRACE_ON(olerelay),
 	    FALSE,
 	    &elem->tdesc,
-	    xargs,
+	    argsptr,
 	    &buf
 	);
 
@@ -1543,7 +1566,7 @@ static DWORD WINAPI xCall(int method, void **args)
 	    ERR("Failed to serialize param, hres %x\n",hres);
 	    break;
 	}
-	xargs+=_argsize(&elem->tdesc, tinfo);
+	xargs += argsize;
     }
     TRACE_(olerelay)(")");
 
@@ -1577,23 +1600,31 @@ static DWORD WINAPI xCall(int method, void **args)
     status = S_OK;
     for (i=0;i<fdesc->cParams;i++) {
 	ELEMDESC	*elem = fdesc->lprgelemdescParam+i;
+	DWORD_PTR	*argsptr;
+	int		argsize, byref;
 
         if (i) TRACE_(olerelay)(",");
         if (i+1<nrofnames && names[i+1]) TRACE_(olerelay)("%s=",relaystr(names[i+1]));
 
+	argsize = _argsize(&elem->tdesc, tinfo, &byref);
 	/* No need to marshal other data than FOUT and any VT_PTR */
 	if (!is_out_elem(elem) && (elem->tdesc.vt != VT_PTR)) {
-	    xargs += _argsize(&elem->tdesc, tinfo);
+	    xargs += argsize;
 	    TRACE_(olerelay)("[in]");
 	    continue;
 	}
+
+	argsptr = xargs;
+	if (byref)
+            argsptr = *(DWORD_PTR**)xargs;
+
 	hres = deserialize_param(
 	    tinfo,
 	    is_out_elem(elem),
 	    TRACE_ON(olerelay),
 	    FALSE,
 	    &(elem->tdesc),
-	    xargs,
+	    argsptr,
 	    &buf
         );
 	if (hres) {
@@ -1601,7 +1632,7 @@ static DWORD WINAPI xCall(int method, void **args)
 	    status = hres;
 	    break;
 	}
-	xargs += _argsize(&elem->tdesc, tinfo);
+	xargs += argsize;
     }
 
     hres = xbuf_get(&buf, (LPBYTE)&remoteresult, sizeof(DWORD));
@@ -1832,7 +1863,7 @@ static HRESULT init_proxy_entry_point(TMProxyImpl *proxy, unsigned int num)
 {
     int j;
     /* nrofargs including This */
-    int nrofargs = 1;
+    int nrofargs = 1, byref;
     ITypeInfo *tinfo2;
     TMAsmProxy	*xasm = proxy->asmstubs + num;
     HRESULT hres;
@@ -1846,7 +1877,7 @@ static HRESULT init_proxy_entry_point(TMProxyImpl *proxy, unsigned int num)
     ITypeInfo_Release(tinfo2);
     /* some args take more than 4 byte on the stack */
     for (j=0;j<fdesc->cParams;j++)
-        nrofargs += _argsize(&fdesc->lprgelemdescParam[j].tdesc, proxy->tinfo);
+        nrofargs += _argsize(&fdesc->lprgelemdescParam[j].tdesc, proxy->tinfo, &byref);
 
 #ifdef __i386__
     if (fdesc->callconv != CC_STDCALL) {
@@ -1872,6 +1903,21 @@ static HRESULT init_proxy_entry_point(TMProxyImpl *proxy, unsigned int num)
 
 #elif defined(__x86_64__)
 
+/* Stack layout:
+ * We store the 4 register arguments in the supplied "register save zone"
+ * so all arguments line up on the stack, same as in i386 mode.
+ * ... further args ...
+ * arg6
+ * arg5
+ * zone0	8	+0x28 arg4
+ * zone0	8	+0x20 arg3
+ * zone0	8	+0x18 arg2
+ * zone0	8	+0x10 arg1	< %RDX (arg2) before call
+ * return RIP	8
+ * saved RBP	8	<= RBP in assembler stub
+ * ..
+ */
+
     xasm->pushq_rbp         = 0x55;         /* pushq %rbp */
     xasm->movq_rsp_rbp[0]   = 0x48;         /* movq %rsp,%rbp */
     xasm->movq_rsp_rbp[1]   = 0x89;
@@ -2124,7 +2170,7 @@ TMStubImpl_Invoke(
     LPRPCSTUBBUFFER iface, RPCOLEMESSAGE* xmsg,IRpcChannelBuffer*rpcchanbuf)
 {
 #if defined(__i386__) || defined(__x86_64__)
-    int		i;
+    int		i, byref;
     const FUNCDESC *fdesc;
     TMStubImpl *This = impl_from_IRpcStubBuffer(iface);
     HRESULT	hres;
@@ -2193,7 +2239,7 @@ TMStubImpl_Invoke(
     /*dump_FUNCDESC(fdesc);*/
     nrofargs = 0;
     for (i=0;i<fdesc->cParams;i++)
-	nrofargs += _argsize(&fdesc->lprgelemdescParam[i].tdesc, tinfo);
+	nrofargs += _argsize(&fdesc->lprgelemdescParam[i].tdesc, tinfo, &byref);
     args = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (nrofargs+1)*sizeof(DWORD_PTR));
     if (!args)
     {
@@ -2205,6 +2251,15 @@ TMStubImpl_Invoke(
     xargs = args+1;
     for (i=0;i<fdesc->cParams;i++) {
 	ELEMDESC	*elem = fdesc->lprgelemdescParam+i;
+	DWORD_PTR	*argsptr;
+	int		argsize, byref;
+
+	argsize = _argsize(&elem->tdesc, tinfo, &byref);
+	argsptr = xargs;
+	if (byref) {
+            *xargs = (DWORD_PTR)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,_xsize(&elem->tdesc, tinfo));
+            argsptr = *(DWORD_PTR**)xargs;
+        }
 
 	hres = deserialize_param(
 	   tinfo,
@@ -2212,10 +2267,10 @@ TMStubImpl_Invoke(
 	   FALSE,
 	   TRUE,
 	   &(elem->tdesc),
-	   xargs,
+	   argsptr,
 	   &buf
 	);
-	xargs += _argsize(&elem->tdesc, tinfo);
+	xargs += argsize;
 	if (hres) {
 	    ERR("Failed to deserialize param %s, hres %x\n",relaystr(names[i+1]),hres);
 	    break;
@@ -2252,16 +2307,26 @@ TMStubImpl_Invoke(
     xargs = args+1;
     for (i=0;i<fdesc->cParams;i++) {
 	ELEMDESC	*elem = fdesc->lprgelemdescParam+i;
+	DWORD_PTR	*argsptr;
+	int		argsize, byref;
+
+	argsize = _argsize(&elem->tdesc, tinfo, &byref);
+	argsptr = xargs;
+	if (byref)
+            argsptr = *(DWORD_PTR**)xargs;
 	hres = serialize_param(
 	   tinfo,
 	   is_out_elem(elem),
 	   FALSE,
 	   TRUE,
 	   &elem->tdesc,
-	   xargs,
+	   argsptr,
 	   &buf
 	);
-	xargs += _argsize(&elem->tdesc, tinfo);
+	if (byref)
+            HeapFree(GetProcessHeap(),HEAP_ZERO_MEMORY,*(DWORD_PTR**)argsptr);
+
+	xargs += argsize;
 	if (hres) {
 	    ERR("Failed to stuballoc param, hres %x\n",hres);
 	    break;
-- 
2.14.3




More information about the wine-patches mailing list