1 /* 2 * COM proxy implementation 3 * 4 * Copyright 2001 Ove Kåven, TransGaming Technologies 5 * Copyright 2009 Alexandre Julliard 6 * 7 * This library is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This library is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this library; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include "config.h" 23 #include "wine/port.h" 24 25 #include <stdarg.h> 26 27 #define COBJMACROS 28 29 #include "windef.h" 30 #include "winbase.h" 31 #include "winerror.h" 32 33 #include "objbase.h" 34 #include "rpcproxy.h" 35 36 #include "cpsf.h" 37 #include "ndr_misc.h" 38 #include "ndr_stubless.h" 39 #include "wine/debug.h" 40 41 WINE_DEFAULT_DEBUG_CHANNEL(ole); 42 43 static const IRpcProxyBufferVtbl StdProxy_Vtbl; 44 45 static inline StdProxyImpl *impl_from_IRpcProxyBuffer(IRpcProxyBuffer *iface) 46 { 47 return CONTAINING_RECORD(iface, StdProxyImpl, IRpcProxyBuffer_iface); 48 } 49 50 static inline StdProxyImpl *impl_from_proxy_obj( void *iface ) 51 { 52 return CONTAINING_RECORD(iface, StdProxyImpl, PVtbl); 53 } 54 55 #ifdef __i386__ 56 57 extern void call_stubless_func(void); 58 __ASM_GLOBAL_FUNC(call_stubless_func, 59 "movl 4(%esp),%ecx\n\t" /* This pointer */ 60 "movl (%ecx),%ecx\n\t" /* This->lpVtbl */ 61 "movl -8(%ecx),%ecx\n\t" /* MIDL_STUBLESS_PROXY_INFO */ 62 "movl 8(%ecx),%edx\n\t" /* info->FormatStringOffset */ 63 "movzwl (%edx,%eax,2),%edx\n\t" /* FormatStringOffset[index] */ 64 "addl 4(%ecx),%edx\n\t" /* info->ProcFormatString + offset */ 65 "movzbl 1(%edx),%eax\n\t" /* Oi_flags */ 66 "andl $0x08,%eax\n\t" /* Oi_HAS_RPCFLAGS */ 67 "shrl $1,%eax\n\t" 68 "movzwl 4(%edx,%eax),%eax\n\t" /* arguments size */ 69 "pushl %eax\n\t" 70 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") 71 "leal 8(%esp),%eax\n\t" /* &This */ 72 "pushl %eax\n\t" 73 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") 74 "pushl %edx\n\t" /* format string */ 75 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") 76 "pushl (%ecx)\n\t" /* info->pStubDesc */ 77 __ASM_CFI(".cfi_adjust_cfa_offset 4\n\t") 78 "call " __ASM_NAME("ndr_client_call") "\n\t" 79 "leal 12(%esp),%esp\n\t" 80 __ASM_CFI(".cfi_adjust_cfa_offset -12\n\t") 81 "popl %edx\n\t" /* arguments size */ 82 __ASM_CFI(".cfi_adjust_cfa_offset -4\n\t") 83 "movl (%esp),%ecx\n\t" /* return address */ 84 "addl %edx,%esp\n\t" 85 "jmp *%ecx" ); 86 87 #include "pshpack1.h" 88 struct thunk 89 { 90 BYTE mov_eax; 91 DWORD index; 92 BYTE jmp; 93 LONG handler; 94 }; 95 #include "poppack.h" 96 97 static inline void init_thunk( struct thunk *thunk, unsigned int index ) 98 { 99 thunk->mov_eax = 0xb8; /* movl $n,%eax */ 100 thunk->index = index; 101 thunk->jmp = 0xe9; /* jmp */ 102 thunk->handler = (char *)call_stubless_func - (char *)(&thunk->handler + 1); 103 } 104 105 #elif defined(__x86_64__) 106 107 extern void call_stubless_func(void); 108 __ASM_GLOBAL_FUNC(call_stubless_func, 109 "movq %rcx,0x8(%rsp)\n\t" 110 "movq %rdx,0x10(%rsp)\n\t" 111 "movq %r8,0x18(%rsp)\n\t" 112 "movq %r9,0x20(%rsp)\n\t" 113 "leaq 0x8(%rsp),%r8\n\t" /* &This */ 114 "movq (%rcx),%rcx\n\t" /* This->lpVtbl */ 115 "movq -0x10(%rcx),%rcx\n\t" /* MIDL_STUBLESS_PROXY_INFO */ 116 "movq 0x10(%rcx),%rdx\n\t" /* info->FormatStringOffset */ 117 "movzwq (%rdx,%r10,2),%rdx\n\t" /* FormatStringOffset[index] */ 118 "addq 8(%rcx),%rdx\n\t" /* info->ProcFormatString + offset */ 119 "movq (%rcx),%rcx\n\t" /* info->pStubDesc */ 120 "subq $0x38,%rsp\n\t" 121 __ASM_CFI(".cfi_adjust_cfa_offset 0x38\n\t") 122 "movq %xmm1,0x20(%rsp)\n\t" 123 "movq %xmm2,0x28(%rsp)\n\t" 124 "movq %xmm3,0x30(%rsp)\n\t" 125 "leaq 0x18(%rsp),%r9\n\t" /* fpu_args */ 126 "call " __ASM_NAME("ndr_client_call") "\n\t" 127 "addq $0x38,%rsp\n\t" 128 __ASM_CFI(".cfi_adjust_cfa_offset -0x38\n\t") 129 "ret" ); 130 131 #include "pshpack1.h" 132 struct thunk 133 { 134 BYTE mov_r10[3]; 135 DWORD index; 136 BYTE mov_rax[2]; 137 void *call_stubless; 138 BYTE jmp_rax[2]; 139 }; 140 #include "poppack.h" 141 142 static const struct thunk thunk_template = 143 { 144 { 0x49, 0xc7, 0xc2 }, 0, /* movq $index,%r10 */ 145 { 0x48, 0xb8 }, 0, /* movq $call_stubless_func,%rax */ 146 { 0xff, 0xe0 } /* jmp *%rax */ 147 }; 148 149 static inline void init_thunk( struct thunk *thunk, unsigned int index ) 150 { 151 *thunk = thunk_template; 152 thunk->index = index; 153 thunk->call_stubless = call_stubless_func; 154 } 155 156 #elif defined(__arm__) 157 158 extern void call_stubless_func(void); 159 __ASM_GLOBAL_FUNC(call_stubless_func, 160 "push {r0-r3}\n\t" 161 "mov r2, sp\n\t" /* stack_top */ 162 "push {fp,lr}\n\t" 163 "mov fp, sp\n\t" 164 "ldr r0, [r0]\n\t" /* This->lpVtbl */ 165 "ldr r0, [r0,#-8]\n\t" /* MIDL_STUBLESS_PROXY_INFO */ 166 "ldr r1, [r0,#8]\n\t" /* info->FormatStringOffset */ 167 "ldrh r1, [r1,ip]\n\t" /* info->FormatStringOffset[index] */ 168 "ldr ip, [r0,#4]\n\t" /* info->ProcFormatString */ 169 "add r1, ip\n\t" /* info->ProcFormatString + offset */ 170 "ldr r0, [r0]\n\t" /* info->pStubDesc */ 171 #ifdef __SOFTFP__ 172 "mov r3, #0\n\t" 173 #else 174 "vpush {s0-s15}\n\t" /* store the s0-s15/d0-d7 arguments */ 175 "mov r3, sp\n\t" /* fpu_stack */ 176 #endif 177 "bl " __ASM_NAME("ndr_client_call") "\n\t" 178 "mov sp, fp\n\t" 179 "pop {fp,lr}\n\t" 180 "add sp, #16\n\t" 181 "bx lr" ); 182 183 struct thunk 184 { 185 DWORD ldr_ip; /* ldr ip,[pc] */ 186 DWORD ldr_pc; /* ldr pc,[pc] */ 187 DWORD index; 188 void *func; 189 }; 190 191 static inline void init_thunk( struct thunk *thunk, unsigned int index ) 192 { 193 thunk->ldr_ip = 0xe59fc000; /* ldr ip,[pc] */ 194 thunk->ldr_pc = 0xe59ff000; /* ldr pc,[pc] */ 195 thunk->index = index * sizeof(unsigned short); 196 thunk->func = call_stubless_func; 197 } 198 199 #else /* __i386__ */ 200 201 #warning You must implement stubless proxies for your CPU 202 203 struct thunk 204 { 205 DWORD index; 206 }; 207 208 static inline void init_thunk( struct thunk *thunk, unsigned int index ) 209 { 210 thunk->index = index; 211 } 212 213 #endif /* __i386__ */ 214 215 #define BLOCK_SIZE 1024 216 #define MAX_BLOCKS 64 /* 64k methods should be enough for anybody */ 217 218 static const struct thunk *method_blocks[MAX_BLOCKS]; 219 220 static const struct thunk *allocate_block( unsigned int num ) 221 { 222 unsigned int i; 223 struct thunk *prev, *block; 224 DWORD oldprot; 225 226 block = VirtualAlloc( NULL, BLOCK_SIZE * sizeof(*block), 227 MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE ); 228 if (!block) return NULL; 229 230 for (i = 0; i < BLOCK_SIZE; i++) init_thunk( &block[i], BLOCK_SIZE * num + i + 3 ); 231 VirtualProtect( block, BLOCK_SIZE * sizeof(*block), PAGE_EXECUTE_READ, &oldprot ); 232 prev = InterlockedCompareExchangePointer( (void **)&method_blocks[num], block, NULL ); 233 if (prev) /* someone beat us to it */ 234 { 235 VirtualFree( block, 0, MEM_RELEASE ); 236 block = prev; 237 } 238 return block; 239 } 240 241 BOOL fill_stubless_table( IUnknownVtbl *vtbl, DWORD num ) 242 { 243 const void **entry = (const void **)(vtbl + 1); 244 DWORD i, j; 245 246 if (num - 3 > BLOCK_SIZE * MAX_BLOCKS) 247 { 248 FIXME( "%u methods not supported\n", num ); 249 return FALSE; 250 } 251 for (i = 0; i < (num - 3 + BLOCK_SIZE - 1) / BLOCK_SIZE; i++) 252 { 253 const struct thunk *block = method_blocks[i]; 254 if (!block && !(block = allocate_block( i ))) return FALSE; 255 for (j = 0; j < BLOCK_SIZE && j < num - 3 - i * BLOCK_SIZE; j++, entry++) 256 if (*entry == (LPVOID)-1) *entry = &block[j]; 257 } 258 return TRUE; 259 } 260 261 HRESULT StdProxy_Construct(REFIID riid, 262 LPUNKNOWN pUnkOuter, 263 const ProxyFileInfo *ProxyInfo, 264 int Index, 265 LPPSFACTORYBUFFER pPSFactory, 266 LPRPCPROXYBUFFER *ppProxy, 267 LPVOID *ppvObj) 268 { 269 StdProxyImpl *This; 270 PCInterfaceName name = ProxyInfo->pNamesArray[Index]; 271 CInterfaceProxyVtbl *vtbl = ProxyInfo->pProxyVtblList[Index]; 272 273 TRACE("(%p,%p,%p,%p,%p) %s\n", pUnkOuter, vtbl, pPSFactory, ppProxy, ppvObj, name); 274 275 /* TableVersion = 2 means it is the stubless version of CInterfaceProxyVtbl */ 276 if (ProxyInfo->TableVersion > 1) { 277 ULONG count = ProxyInfo->pStubVtblList[Index]->header.DispatchTableCount; 278 vtbl = (CInterfaceProxyVtbl *)((const void **)vtbl + 1); 279 TRACE("stubless vtbl %p: count=%d\n", vtbl->Vtbl, count ); 280 fill_stubless_table( (IUnknownVtbl *)vtbl->Vtbl, count ); 281 } 282 283 if (!IsEqualGUID(vtbl->header.piid, riid)) { 284 ERR("IID mismatch during proxy creation\n"); 285 return RPC_E_UNEXPECTED; 286 } 287 288 This = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(StdProxyImpl)); 289 if (!This) return E_OUTOFMEMORY; 290 291 if (!pUnkOuter) pUnkOuter = (IUnknown *)This; 292 This->IRpcProxyBuffer_iface.lpVtbl = &StdProxy_Vtbl; 293 This->PVtbl = vtbl->Vtbl; 294 /* one reference for the proxy */ 295 This->RefCount = 1; 296 This->piid = vtbl->header.piid; 297 This->base_object = NULL; 298 This->base_proxy = NULL; 299 This->pUnkOuter = pUnkOuter; 300 This->name = name; 301 This->pPSFactory = pPSFactory; 302 This->pChannel = NULL; 303 304 if(ProxyInfo->pDelegatedIIDs && ProxyInfo->pDelegatedIIDs[Index]) 305 { 306 HRESULT r = create_proxy( ProxyInfo->pDelegatedIIDs[Index], NULL, 307 &This->base_proxy, (void **)&This->base_object ); 308 if (FAILED(r)) 309 { 310 HeapFree( GetProcessHeap(), 0, This ); 311 return r; 312 } 313 } 314 315 *ppProxy = &This->IRpcProxyBuffer_iface; 316 *ppvObj = &This->PVtbl; 317 IUnknown_AddRef((IUnknown *)*ppvObj); 318 IPSFactoryBuffer_AddRef(pPSFactory); 319 320 TRACE( "iid=%s this %p proxy %p obj %p vtbl %p base proxy %p base obj %p\n", 321 debugstr_guid(riid), This, *ppProxy, *ppvObj, This->PVtbl, This->base_proxy, This->base_object ); 322 return S_OK; 323 } 324 325 HRESULT WINAPI StdProxy_QueryInterface(IRpcProxyBuffer *iface, REFIID riid, void **obj) 326 { 327 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface); 328 TRACE("(%p)->QueryInterface(%s,%p)\n",This,debugstr_guid(riid),obj); 329 330 if (IsEqualGUID(&IID_IUnknown,riid) || 331 IsEqualGUID(This->piid,riid)) { 332 *obj = &This->PVtbl; 333 InterlockedIncrement(&This->RefCount); 334 return S_OK; 335 } 336 337 if (IsEqualGUID(&IID_IRpcProxyBuffer,riid)) { 338 *obj = &This->IRpcProxyBuffer_iface; 339 InterlockedIncrement(&This->RefCount); 340 return S_OK; 341 } 342 343 return E_NOINTERFACE; 344 } 345 346 ULONG WINAPI StdProxy_AddRef(IRpcProxyBuffer *iface) 347 { 348 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface); 349 TRACE("(%p)->AddRef()\n",This); 350 351 return InterlockedIncrement(&This->RefCount); 352 } 353 354 static ULONG WINAPI StdProxy_Release(LPRPCPROXYBUFFER iface) 355 { 356 ULONG refs; 357 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface); 358 TRACE("(%p)->Release()\n",This); 359 360 refs = InterlockedDecrement(&This->RefCount); 361 if (!refs) 362 { 363 if (This->pChannel) 364 IRpcProxyBuffer_Disconnect(&This->IRpcProxyBuffer_iface); 365 366 if (This->base_object) IUnknown_Release( This->base_object ); 367 if (This->base_proxy) IRpcProxyBuffer_Release( This->base_proxy ); 368 369 IPSFactoryBuffer_Release(This->pPSFactory); 370 HeapFree(GetProcessHeap(),0,This); 371 } 372 373 return refs; 374 } 375 376 HRESULT WINAPI StdProxy_Connect(IRpcProxyBuffer *iface, IRpcChannelBuffer *pChannel) 377 { 378 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface); 379 TRACE("(%p)->Connect(%p)\n",This,pChannel); 380 381 This->pChannel = pChannel; 382 IRpcChannelBuffer_AddRef(pChannel); 383 if (This->base_proxy) IRpcProxyBuffer_Connect( This->base_proxy, pChannel ); 384 return S_OK; 385 } 386 387 void WINAPI StdProxy_Disconnect(IRpcProxyBuffer *iface) 388 { 389 StdProxyImpl *This = impl_from_IRpcProxyBuffer(iface); 390 TRACE("(%p)->Disconnect()\n",This); 391 392 if (This->base_proxy) IRpcProxyBuffer_Disconnect( This->base_proxy ); 393 394 IRpcChannelBuffer_Release(This->pChannel); 395 This->pChannel = NULL; 396 } 397 398 static const IRpcProxyBufferVtbl StdProxy_Vtbl = 399 { 400 StdProxy_QueryInterface, 401 StdProxy_AddRef, 402 StdProxy_Release, 403 StdProxy_Connect, 404 StdProxy_Disconnect 405 }; 406 407 static void StdProxy_GetChannel(LPVOID iface, 408 LPRPCCHANNELBUFFER *ppChannel) 409 { 410 StdProxyImpl *This = impl_from_proxy_obj( iface ); 411 TRACE("(%p)->GetChannel(%p) %s\n",This,ppChannel,This->name); 412 413 *ppChannel = This->pChannel; 414 } 415 416 static void StdProxy_GetIID(LPVOID iface, 417 const IID **ppiid) 418 { 419 StdProxyImpl *This = impl_from_proxy_obj( iface ); 420 TRACE("(%p)->GetIID(%p) %s\n",This,ppiid,This->name); 421 422 *ppiid = This->piid; 423 } 424 425 HRESULT WINAPI IUnknown_QueryInterface_Proxy(LPUNKNOWN iface, 426 REFIID riid, 427 LPVOID *ppvObj) 428 { 429 StdProxyImpl *This = impl_from_proxy_obj( iface ); 430 TRACE("(%p)->QueryInterface(%s,%p) %s\n",This,debugstr_guid(riid),ppvObj,This->name); 431 return IUnknown_QueryInterface(This->pUnkOuter,riid,ppvObj); 432 } 433 434 ULONG WINAPI IUnknown_AddRef_Proxy(LPUNKNOWN iface) 435 { 436 StdProxyImpl *This = impl_from_proxy_obj( iface ); 437 TRACE("(%p)->AddRef() %s\n",This,This->name); 438 return IUnknown_AddRef(This->pUnkOuter); 439 } 440 441 ULONG WINAPI IUnknown_Release_Proxy(LPUNKNOWN iface) 442 { 443 StdProxyImpl *This = impl_from_proxy_obj( iface ); 444 TRACE("(%p)->Release() %s\n",This,This->name); 445 return IUnknown_Release(This->pUnkOuter); 446 } 447 448 /*********************************************************************** 449 * NdrProxyInitialize [RPCRT4.@] 450 */ 451 void WINAPI NdrProxyInitialize(void *This, 452 PRPC_MESSAGE pRpcMsg, 453 PMIDL_STUB_MESSAGE pStubMsg, 454 PMIDL_STUB_DESC pStubDescriptor, 455 unsigned int ProcNum) 456 { 457 TRACE("(%p,%p,%p,%p,%d)\n", This, pRpcMsg, pStubMsg, pStubDescriptor, ProcNum); 458 NdrClientInitializeNew(pRpcMsg, pStubMsg, pStubDescriptor, ProcNum); 459 StdProxy_GetChannel(This, &pStubMsg->pRpcChannelBuffer); 460 IRpcChannelBuffer_GetDestCtx(pStubMsg->pRpcChannelBuffer, 461 &pStubMsg->dwDestContext, 462 &pStubMsg->pvDestContext); 463 TRACE("channel=%p\n", pStubMsg->pRpcChannelBuffer); 464 } 465 466 /*********************************************************************** 467 * NdrProxyGetBuffer [RPCRT4.@] 468 */ 469 void WINAPI NdrProxyGetBuffer(void *This, 470 PMIDL_STUB_MESSAGE pStubMsg) 471 { 472 HRESULT hr; 473 const IID *riid = NULL; 474 475 TRACE("(%p,%p)\n", This, pStubMsg); 476 pStubMsg->RpcMsg->BufferLength = pStubMsg->BufferLength; 477 pStubMsg->dwStubPhase = PROXY_GETBUFFER; 478 StdProxy_GetIID(This, &riid); 479 hr = IRpcChannelBuffer_GetBuffer(pStubMsg->pRpcChannelBuffer, 480 (RPCOLEMESSAGE*)pStubMsg->RpcMsg, 481 riid); 482 if (FAILED(hr)) 483 { 484 RpcRaiseException(hr); 485 return; 486 } 487 pStubMsg->fBufferValid = TRUE; 488 pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer; 489 pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength; 490 pStubMsg->Buffer = pStubMsg->BufferStart; 491 pStubMsg->dwStubPhase = PROXY_MARSHAL; 492 } 493 494 /*********************************************************************** 495 * NdrProxySendReceive [RPCRT4.@] 496 */ 497 void WINAPI NdrProxySendReceive(void *This, 498 PMIDL_STUB_MESSAGE pStubMsg) 499 { 500 ULONG Status = 0; 501 HRESULT hr; 502 503 TRACE("(%p,%p)\n", This, pStubMsg); 504 505 if (!pStubMsg->pRpcChannelBuffer) 506 { 507 WARN("Trying to use disconnected proxy %p\n", This); 508 RpcRaiseException(RPC_E_DISCONNECTED); 509 } 510 511 pStubMsg->dwStubPhase = PROXY_SENDRECEIVE; 512 /* avoid sending uninitialised parts of the buffer on the wire */ 513 pStubMsg->RpcMsg->BufferLength = pStubMsg->Buffer - (unsigned char *)pStubMsg->RpcMsg->Buffer; 514 hr = IRpcChannelBuffer_SendReceive(pStubMsg->pRpcChannelBuffer, 515 (RPCOLEMESSAGE*)pStubMsg->RpcMsg, 516 &Status); 517 pStubMsg->dwStubPhase = PROXY_UNMARSHAL; 518 pStubMsg->BufferLength = pStubMsg->RpcMsg->BufferLength; 519 pStubMsg->BufferStart = pStubMsg->RpcMsg->Buffer; 520 pStubMsg->BufferEnd = pStubMsg->BufferStart + pStubMsg->BufferLength; 521 pStubMsg->Buffer = pStubMsg->BufferStart; 522 523 /* raise exception if call failed */ 524 if (hr == RPC_S_CALL_FAILED) RpcRaiseException(*(DWORD*)pStubMsg->Buffer); 525 else if (FAILED(hr)) RpcRaiseException(hr); 526 } 527 528 /*********************************************************************** 529 * NdrProxyFreeBuffer [RPCRT4.@] 530 */ 531 void WINAPI NdrProxyFreeBuffer(void *This, 532 PMIDL_STUB_MESSAGE pStubMsg) 533 { 534 TRACE("(%p,%p)\n", This, pStubMsg); 535 536 if (pStubMsg->fBufferValid) 537 { 538 IRpcChannelBuffer_FreeBuffer(pStubMsg->pRpcChannelBuffer, 539 (RPCOLEMESSAGE*)pStubMsg->RpcMsg); 540 pStubMsg->fBufferValid = TRUE; 541 } 542 } 543 544 /*********************************************************************** 545 * NdrProxyErrorHandler [RPCRT4.@] 546 */ 547 HRESULT WINAPI NdrProxyErrorHandler(DWORD dwExceptionCode) 548 { 549 WARN("(0x%08x): a proxy call failed\n", dwExceptionCode); 550 551 if (FAILED(dwExceptionCode)) 552 return dwExceptionCode; 553 else 554 return HRESULT_FROM_WIN32(dwExceptionCode); 555 } 556