Page 1 of 1

Calling a C++ Member function from ASM

Posted: Sun Jul 20, 2008 6:32 am
by pacman
Hi

I would like to know - is it possible to access a C++ private or public function, which is a member of a dynamic object (correct terminology?) from assembly code?

Code: Select all

extern "C" {void function();}
class MyClass
{
     private:
     void function();
}

void main()
{
     MyClass test = new myClass();
}
ASM:

Code: Select all

_asmFunction:
     mov eax, <<Need to place test->function() in here>>
     call eax

Re: Calling a C++ Member function from ASM

Posted: Sun Jul 20, 2008 7:27 am
by Korona
It is possible but there is no portable way of doing that.
First you have to find out the mangled name of your method. (I don't think extern "C" works on member functions but I might be wrong) The name mangling scheme depends on the compiler you use. Look at the assembly code your compiler generates for your method (via the -S command line flag in gcc) and find out the mangled name.
After you know that symbol you can call your function from assembly. Note that you also have to pass an object pointer to it. AFAIK if you're using gcc the object pointer will have to be pushed on the stack as the last argument. You could disassemble a c++ file to make sure that you pass the object pointer correctly.
EDIT: Note: If you're calling a virtual method, you have to lookup the method address in the vtables.

Re: Calling a C++ Member function from ASM

Posted: Sun Jul 20, 2008 8:39 am
by AJ
If you get this working, remember that the first parameter for any C++ function should also be a pointer to this.

I wonder if you may have more luck with a static member? You could then assign the static member to an extern "C" function pointer and call that from your ASM.

Cheers,
Adam

Re: Calling a C++ Member function from ASM

Posted: Sun Jul 20, 2008 10:45 am
by kmcguire
You could cause the compiler to not emit a mangled name for the class method by doing something like:

Code: Select all

class MyObject
{
	public:
	MyObject()
	{
	}
	~MyObject()
	{
	}
	int Method1() asm ("myfoo");
};

int MyObject::Method1()
{
	printf("haha\n");
	return 5;
}
Then, you can call the method from assembly:

Code: Select all

.global test
.extern myfoo
test:
call myfoo
I used this to start the example:

Code: Select all

extern "C" void test(void);

int main(int argc, char *argv[])
{
	printf("start\n");
	test();
	printf("end\n");
	return 1;
}
I forgot to mention that this very well might not be portable to other compilers, like MSVC. This does work for the GNU C/C++ compiler.

Re: Calling a C++ Member function from ASM

Posted: Mon Jul 21, 2008 12:35 pm
by Candy
Just a minor thing, he's asking for a virtual function call. Not a regular function call.

Code: Select all

; void CallVirtualFunction(int funcno, void *obj, ...)
CallVirtualFunction:
    mov eax, [esp+8]
    mov eax, [eax]
    mov esi, [esp+4]
    mov eax, [eax+esi*4]
    mov esi, [esp]
    add esp, 4
    mov [esp], esi
    call *eax
    mov esi, [esp]
    sub esp, 4
    mov [esp], esi
    ret
just about that should do it. The first mov loads the obj pointer into eax, the second loads the first item from obj (the virtual function table pointer) into eax. The third swaps esi with the function index and the fourth loads the function pointer with number esi into eax. It then adjusts the stack to not have the function number on the stack. When the call returns, it moves it back and returns to the caller.

There's a flaw in the code and I can't be bothered to remove it. Kudos to the one that finds it.

Re: Calling a C++ Member function from ASM

Posted: Mon Jul 21, 2008 1:44 pm
by Korona

Code: Select all

; void CallVirtualFunction(int funcno, void *obj, ...)
CallVirtualFunction:
    mov eax, [esp+8]
    mov eax, [eax]
    mov esi, [esp+4]
    mov eax, [eax+esi*4]
    mov edi, [esp]
    mov [saved_retaddr], edi
    add esp, 4
    mov edi, [esp]
    mov [saved_funcno], edi
    mov [esp], dword return_here
    jmp *eax
return_here:
   push dword [saved_funcno] ; use registers that are guaranteed to be preserved instead of static memory locations if this should be thread safe
   jmp [saved_retaddr]
saved_funcno: dd 0
saved_retaddr: dd 0
Your call instruction pushes a second return address onto the stack. After the call there are two return addresses on the stack. The C++ function expects that there is only one return address so it messes up its arguments.

Re: Calling a C++ Member function from ASM

Posted: Mon Jul 21, 2008 5:34 pm
by froggey
Candy wrote:There's a flaw in the code and I can't be bothered to remove it. Kudos to the one that finds it.
You're trashing esi, a callee-save register.
Changing the call to a tail-call (jmp) won't work because the caller is expecting to pop n arguments, not n-1 argument and the arguments can't be moved about on the stack because we don't know how many there are. Korona's fix is non-reentrant. The only "nice" solution i can think of is to inline this function.

Re: Calling a C++ Member function from ASM

Posted: Tue Jul 22, 2008 2:59 am
by Korona
I already said that my function is non-reentrant. I also proposed a solution for that. Read my example again. Trashing esi is not an issue as this code is going to be called from assembly.

Re: Calling a C++ Member function from ASM

Posted: Tue Jul 22, 2008 6:01 am
by froggey
I see what you mean, Candy's doesn't set the stack arguments up correctly for the called function, leaving funcno/saved return address on the stack, that seems a bit more that a "minor thing" ;)

My untested, thread-safe, reentrant solution:

Code: Select all

CallVirtualFunction:
    mov eax, [esp+8]
    mov eax, [eax]
    mov esi, [esp+4]
    mov eax, [eax+esi*4]
    pop esi              # save return address
    add esp, 4           # Drop funcno
    call *eax            # Call virtual function with arguments
    sub esp, 4           # create fake funcno argument
    push esi             # restore return address
    ret
Expects that both the caller and the callee save esi. funcno has to be on the stack at return so the caller pops the correct number of arguments

Inlineable version, replacing funcno with the actual function number:

Code: Select all

    mov eax, [object]
    call *[eax+funcno*4]

Re: Calling a C++ Member function from ASM

Posted: Mon Jul 28, 2008 10:51 am
by niteice
Would it be easier to write a flat wrapper function:

Code: Select all

class myClass
{
        void someFunc(int arg1, char *arg2);
};

void _wrap_myClass_someFunc(myClass *_this, int arg1, char *arg2)
{
        _this->someFunc(arg1, arg2);
}
Then get a pointer to the object and call that from asm?