Calling a C++ Member function from ASM

Programming, for all ages and all languages.
Post Reply
pacman
Posts: 17
Joined: Sun Jul 08, 2007 10:23 am

Calling a C++ Member function from ASM

Post 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
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Re: Calling a C++ Member function from ASM

Post 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.
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Re: Calling a C++ Member function from ASM

Post 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
User avatar
kmcguire
Member
Member
Posts: 120
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Re: Calling a C++ Member function from ASM

Post 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.
Image
User avatar
Candy
Member
Member
Posts: 3882
Joined: Tue Oct 17, 2006 11:33 pm
Location: Eindhoven

Re: Calling a C++ Member function from ASM

Post 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.
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Re: Calling a C++ Member function from ASM

Post 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.
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].
froggey
Member
Member
Posts: 38
Joined: Tue Oct 17, 2006 10:21 pm
Location: Hampshire, UK

Re: Calling a C++ Member function from ASM

Post 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.
Korona
Member
Member
Posts: 1000
Joined: Thu May 17, 2007 1:27 pm
Contact:

Re: Calling a C++ Member function from ASM

Post 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.
managarm: Microkernel-based OS capable of running a Wayland desktop (Discord: https://discord.gg/7WB6Ur3). My OS-dev projects: [mlibc: Portable C library for managarm, qword, Linux, Sigma, ...] [LAI: AML interpreter] [xbstrap: Build system for OS distributions].
froggey
Member
Member
Posts: 38
Joined: Tue Oct 17, 2006 10:21 pm
Location: Hampshire, UK

Re: Calling a C++ Member function from ASM

Post 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]
niteice
Member
Member
Posts: 59
Joined: Tue Oct 03, 2006 3:49 pm

Re: Calling a C++ Member function from ASM

Post 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?
Post Reply