You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: jekyll/_posts/2018-03-12-steam-api-calls-forwarding.md
+10-6Lines changed: 10 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -48,10 +48,14 @@ Judging by the log, our wrapper works (!) exactly until the function `SteamInter
48
48
49
49
I think that those who are familiar with ABI C ++ already understand the crash origin. The root of the problem is the calling conventions. The C ++ standard does not imply the binary compatibility of programs compiled by different compilers, and in our case the Windows game is compiled by MSVC, while native Steam - by GCC. This problem is not observed for all calls to functions in `steam_api.dll` since they follow the calling conventions for the C language. Once a game receives an instance of the `SteamClient` class from the native Steam and tries to invoke its method (which follows the thiscall convention from C++), an error occurs. To fix the problem, we firstly need to identify key differences in the thiscall calling convention for both of compilers.
50
50
51
-
MSVC | GCC
52
-
---------|--------
53
-
Puts an object pointer to the ECX register. | Expects an object pointer on top of the stack.
54
-
Expects the stack cleanup by callee. | Expects the stack cleanup by caller.
@@ -248,12 +252,12 @@ It does not look very simple, but it's only because we swung a lot at once. Ther
248
252
249
253
Now let's go to the most delicious part - to the code generation. Since we do not have complete information about method signatures, we will emulate instances of classes in C code, fortunately we only need to emulate the virtual method table. So, let's imagine that we have a file that describes the methods and classes of the Steam API as follows:
250
254
251
-
<pre>
255
+
```
252
256
!CAdapterSteamYYY0XX
253
257
[+]<the stack depth for the first method>
254
258
[+]<the stack depth for the second method>
255
259
...
256
-
</pre>
260
+
```
257
261
258
262
The `+` sign is optional and will serve as an indicator of the hidden argument for the in-memory return.
259
263
Such a file can be obtained by parsing `steamclient.so`. We should get a table from it. The keys of the table are lines following the `CAdapterSteamYYYY0XX` pattern, and the values are arrays of functions that call corresponding methods in the object, which is the field of the wrapper structure implicitly passed to them via the `ECX` register. It is not very convenient to write all this methods in assembler, especially considering that it would be nice to add some kind of journaling, so let's find the minimum assembler fragment:
0 commit comments