1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! COM vtable-method dispatch — round 25, stage 2/3 helper.
//!
//! Every COM object is laid out as `[lpVtbl, …per-instance fields…]`,
//! where `lpVtbl` is itself an array of `void(*)(…)` function
//! pointers indexed by interface slot. To call a method we:
//!
//! 1. Read `[obj]` to get the vtable VA.
//! 2. Read `[vtable + 4*slot]` to get the method's guest VA.
//! 3. Push `(this, …args)` right-to-left onto the guest stack
//! (stdcall convention: callee cleans the stack).
//! 4. Push the synthetic `RET_SENTINEL` so the run loop knows
//! when the callee has returned.
//! 5. Run the emulator until `eip == RET_SENTINEL`.
//! 6. Read `eax` for the return value (HRESULT for the methods
//! we drive in round-25; a refcount for `AddRef` / `Release`).
//!
//! The `this` pointer is always the first stdcall argument. This
//! is the same convention MIDL emits for C++ `__stdcall` virtual
//! methods on Windows.
//!
//! Reference: Itanium / MSVC C++ ABI for vtable layout, plus
//! Microsoft's "stdcall calling convention" page on MSDN for the
//! argument-push order.
use ;
use crate;
use crate;
/// Call vtable slot `slot` on object `obj` with `extra_args` as
/// the post-`this` stdcall arguments. Returns the dword in `eax`
/// at the point the callee returned to the synthetic
/// `RET_SENTINEL` (the COM-method HRESULT for most methods).
///
/// `extra_args` are pushed right-to-left, then `this = obj`, then
/// the synthetic return-address sentinel — i.e. the same calling
/// convention `crate::win32::call_guest` already implements, so
/// this is a thin wrapper that prepends `obj` as the first arg.
///
/// For `Release` / `AddRef` `extra_args` is `&[]`. For
/// `QueryInterface(REFIID, void**)` it is `&[piid_ptr,
/// out_ptr_ptr]`. See the SLOT_* constants in the parent module
/// for the documented slot indices and signatures.
/// Convenience wrapper for `IUnknown::AddRef()`. Returns the new
/// refcount the callee reports.
/// Convenience wrapper for `IUnknown::Release()`. Returns the
/// new refcount the callee reports (which is also our signal that
/// the object has been destroyed when it returns 0).
/// `IUnknown::QueryInterface(REFIID, void**)`.
///
/// The IID must already be staged as 16 bytes at `iid_addr` in
/// guest memory (see [`super::Guid::stage`]). `out_ptr_ptr` must
/// be a writable 4-byte slot the callee can store the resulting
/// interface pointer into.
///
/// Returns the HRESULT (`S_OK = 0` on success, `E_NOINTERFACE` if
/// the object does not satisfy that IID). On success the caller
/// reads `[out_ptr_ptr]` to recover the new interface pointer.
/// Pre-flight check: does `obj`'s vtable look "real" (= the first
/// 12 bytes are three readable function pointers, all in mapped
/// memory)? Used by tests to bail early when a codec returned a
/// zeroed-out interface stub instead of a real object.