views:

189

answers:

3

If you want to call a C/C++ function from inline assembly, you can do something like this:

void callee() {}
void caller()
{
    asm("call *%0" : : "r"(callee));
}

GCC will then emit code which looks like this:

movl $callee, %eax
call *%eax

This can be problematic since the indirect call will destroy the pipeline on older CPUs.

Since the address of callee is eventually a constant, one can imagine that it would be possible to use the i constraint. Quoting from the GCC online docs:

`i'

An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time or later.

If I try to use it like this:

asm("call %0" : : "i"(callee));

I get the following error from the assembler:

Error: suffix or operands invalid for `call'

This is because GCC emits the code

call $callee

Instead of

call callee

So my question is whether it is possible to make GCC output the correct call.

A: 

How about.

asm volatile ("call $callee");

since you know the name of the symbol, anyhow.

Edit: The point I wanted to make is that you don't have to go through gcc's conventions for asm arguments here, but that you just have to do text processing to do the job.

Jens Gustedt
Well, that would be my last resort (it should be `asm("call callee");`, though). The problem is that you would need the mangled symbol name. This is not a big problem in C but a PITA in C++.
Job
This should be `asm("call callee");`, but in C++ this would be something like `asm("call _ZL4callv");`
Luther Blissett
This does not work, for several reasons. One, different operating systems (on the same hardware) have different conventions for how symbol names get mapped (the most common oddity is prepending an underscore). And two, unless gcc is somehow told that the asm uses the function, it may very well never omit a standalone callable version of the function. For example it may inline it everywhere else it's used, or may omit it entirely if it's never called from C code. Three, there are probably issues with PIC and PIE code...
R..
The C++ mangling should not be a big deal, I think, since I suspect this is for global functions or `static` members only. (Can't imagine that you want to prepare the arguments for a genuine member function.) You might then just add `extern "C"` to it or go through a function pointer. For the other calling convention problem with underscores or alike, yes, if you want to generalize this you'd have to pack it in a macro that takes this into account.
Jens Gustedt
@Jens Gustedt: Well, imagine a little harder:-) That's exactly what I'm trying to do.
Job
A: 

The trick is string literal concatenation. Before GCC starts trying to get any real meaning from your code it will concatenate adjacent string literals, so even though assembly strings aren't the same as other strings you use in your program they should be concatenated if you do:

#define ASM_CALL(X) asm("\t call  " X "\n")


int main(void) {
    ASM_CALL( "my_function" );
    return 0;
}

Since you are using GCC you could also do

#define ASM_CALL(X) asm("\t call  " #X "\n")

int main(void) {
   ASM_CALL(my_function);
   return 0;
}

If you don't already know you should be aware that calling things from inline assembly is very tricky. When the compiler generates its own calls to other functions it includes code to set up and restore things before and after the call. It doesn't know that it should be doing any of this for your call, though. You will have to either include that yourself (very tricky to get right and may break with a compiler upgrade or compilation flags) or ensure that your function is written in such a way that it does not appear to have changed any registers or condition of the stack (or variable on it).

edit this will only work for C function names -- not C++ as they are mangled.

nategoose
No, this doesn't work at all. First, the '$' is totally wrong for call, second he does not want "my_function" in the asm string, but the **mangled name that C++ generates**.
Luther Blissett
I did not see that this was for C++ too. In that case it is likely that this won't work if the function name is overloaded without lots of hoops. I just copied the "$" from another post since I didn't remember off hand the x86 asm syntax. Fixing that...
nategoose
A: 

I got the answer from GCC's mailing list:

asm("call %P0" : : "i"(callee));

Now I just need to find out what %P0 actually means because it seems to be an undocumented feature...

Edit: After looking at the GCC source code, it's not exactly clear what the code P in front of a constraint means. But, among other things, it prevents GCC from putting a $ in front of constant values. Which is exactly what I need in this case.

Job