Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

emulating thiscall in C to achieve struct functions without self-referencing

This is relating to the flurry of object-oriented C postings, but differs in that I don't want all the features, just one:

An ability to do this:

struct foo { 
  int (*bar)(void);
  char baz;
};

An then have the back-reference. C has a calling convention called cdecl; basically pushes arguments on to the stack, has a return pointer, then jumps to some address. The code at that address pops arguments off the stack and goes on its merry way.

The thiscall convention is slightly different in that it adds one extra argument, a "this" pointer, implicitly.

Since you can start executing arbitrary byte code in C quite easily AND since gcc supports inline assembler templating, this sounds like you could just make some macro so that you could do something like:

int bar(void) {
  GETTHIS;
  cThis->baz = 0;
}

int createFoo(struct Foo*pFoo) {
  ASSIGN(pFoo, bar);
} 

Basically what ASSIGN would do is sidestep cdecl in someway to emulate a thiscall style convention and then what GETTHIS would do is the other side of the accounting trick.

I was wondering if:

  • A solution to this exists and
  • If not, if there's a reason why it can't be done

Just this alone; the convenience of true "We're all consenting adults here" style member functions, would be simply awesome. Thanks!

Notes:

  • I'm just talking x86, linux, gcc here ... I know the world is a wide and strange place.
  • This is out of pure curiosity.
like image 226
kristopolous Avatar asked Dec 22 '11 21:12

kristopolous


3 Answers

My friend eventually got it: https://gist.github.com/1516195 ... requires specifying function arity and is limited to x86_64 ... but yes, it's a pretty nice compromise that makes things really unobtrusive.

like image 110
kristopolous Avatar answered Nov 13 '22 01:11

kristopolous


First little correction:

C has a calling convention called cdecl; basically pushes arguments on to the stack, has a return pointer, then jumps to some address. The code at that address pops arguments off the stack and goes on its merry way.

The callee does not pop the arguments from the stack. It reads the parameters without popping them from the stack. Instead the stack is cleared by the caller. This is so because in cdecl convention the callee doesn't know the exact number of parameters.

Now, regarding your question. There's no strictly defined thiscall calling convention. More info here.

Gcc-specific:

thiscall is almost identical to cdecl: the calling function cleans the stack, and the parameters are passed in right-to-left order. The difference is the addition of the this pointer, which is pushed onto the stack last, as if it were the first parameter in the function prototype

You'll not be able to do your trick with concealing the extra this argument and retrieving it inside the function body, specifically because this is the first function argument. If you conceal it - all the other function parameters will be shifted. Instead you may (and should IMHO) declare it as the first function argument, and you'll have the direct access to it, without the need in extra tricks:

int bar(struct foo *cThis) {
  cThis->baz = 0;
}

Msvc-specific:

... the this pointer is passed in ECX and it is the callee that cleans the stack, mirroring the stdcall convention used in C for this compiler and in Windows API functions. When functions use a variable number of arguments, it is the caller that cleans the stack (cf. cdecl).

Here you may do your trick by copying the value of the ECX register into your local variable. Something like this:

#define GETTHIS \
struct foo *cThis; \
_asm { \
    mov cThis, ecx \
};

This however is tricky and may not always work. The reason for this is that according to the thiscall/stdcall calling conventions the ECX register is reserved for function use, hence the compiler may generated the code that overwrites the value of ECX register. This may even happen before you invoke the GETTHIS macro.

like image 37
valdo Avatar answered Nov 13 '22 01:11

valdo


No, there is no support for "thiscall" in C, that is just for C++.

There is no secret trick here either, just some syntactic sugar.

When you write

class foo {
public:
    int bar();
    char baz;
 }; 

the compiler conceptually rewrites this as

struct foo {
    char baz;
 }; 

 int bar(struct foo* this);

and then when you do

foo   F;
F.bar();

this is compiled as the equivalent of

struct foo   F;
bar(&F);

That's all there is to it!

If you want to play with Linus, you will just have to do this by hand because he doesn't trust you not to use virtual inheritance as soon as you type g++ instead of gcc.

like image 3
Bo Persson Avatar answered Nov 13 '22 01:11

Bo Persson