If you're trying to write an in-process bot using the GNU C or C++ Compiler, you'll run into a problem sooner or later: The GCC calling convention for member functions (thiscall) is different to MS VC++ which means that you can't just call member functions like CGPlayer::ClickToMove without a workaround.
First, the particular differences for thiscall are:
MS Visual C++:
* Arguments are pushed on the stack in reverse order.
* The object pointer is passed in ECX.
* The callee pops arguments when returning.
* Primitive data types, except floating point values, are returned in EAX or EAX:EDX depending on the size.
* float and double are returned in fp0, i.e. the first floating point register.
* Simple data structures with 8 bytes or less in size are returned in EAX:EDX.
* All classes and structures are returned in memory, regardless of size.
* When a return is made in memory the caller passes a pointer to the memory location as the first parameter (hidden). The callee populates the memory, and returns the pointer. The callee pops the hidden pointer together with the rest of the arguments.
* If the method takes variable number of arguments, i.e. is declared with the ... operator, then the calling convention instead becomes that of cdecl. The object pointer is pushed on the stack as the first argument instead of passed in ECX, and all arguments are popped from the stack by the caller when the function returns.
MinGW g++ / Win32:
* Arguments are pushed on the stack in reverse order.
* The object pointer is pushed on the stack as the first parameter.
* The caller pops arguments after return.
* Primitive data types, except floating point values, are returned in EAX or EAX:EDX depending on the size.
* float and double are returned in fp0, i.e. the first floating point register.
* Objects with 8 bytes or less in size are returned in EAX:EDX.
* Objects larger than 8 bytes are returned in memory.
* Classes that have a destructor are returned in memory regardless of size.
* When a return is made in memory the caller passes a pointer to the memory location as the first parameter (hidden). The callee populates the memory, and returns the pointer. The callee pops the hidden pointer from the stack when returning.
* Classes that have a destructor are always passed by reference, even if the parameter is defined to be by value.
GCC g++ / Linux:
* Arguments are pushed on the stack in reverse order.
* The object pointer is pushed on the stack as the first parameter.
* The caller pops arguments after return.
* Primitive data types, except floating point values, are returned in EAX or EAX:EDX depending on the size.
* float and double are returned in fp0, i.e. the first floating point register.
* All structures and classes are returned in memory regardless of complexity and size.
* When a return is made in memory the caller passes a pointer to the memory location as the first parameter (hidden). The callee populates the memory, and returns the pointer. The callee pops the hidden pointer from the stack when returning.
* Classes that have a destructor are always passed by reference, even if the parameter is defined to be by value.
I will use the ClickToMove function as an example for my workaround. The MS VC++ definition of ClickToMove is:
Code:
BOOL __thiscall CGPlayer_C__ClickToMove(
WoWActivePlayer *this, CLICKTOMOVETYPE clickType,
WGUID *interactGuid, WOWPOS *clickPos, float precision);
The first difference I'm compensating for is that MS VC++ code, like WoW, expects the callee to pop the arguments from the stack before it returns. This can be achieved by declaring the function with the attribute stdcall:
Code:
typedef bool (*fp_CGPlayer_C__ClickToMove) (
uint32_t ctmtype, uint64_t* clickGUID,
float* clickPos, float precision)
__attribute__((stdcall));
Note that the function call is missing the instance pointer "WoWActivePlayer *this". As MS VC++ member functions expect that pointer to be in the "ecx" register, I'm setting it with inline assember before the function call.
Code:
asm("movl %0, %%ecx" : : "m" (activePlayer));
ClickToMove(ctmType, pClickGUID, pClickPos, 0);
Note that GCC uses AT&T and not Intel assembler syntax. (opcode src,dest instead of dst,src).
Sources:
* Calling conventions on the x86 platform
* Function Attributes - Using the GNU Compiler Collection (GCC)
* Extended Asm - Using the GNU Compiler Collection (GCC)
This works fine for me. Constructive criticism is welcome.