16

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.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
mtvec
  • 17,846
  • 5
  • 52
  • 83
  • 1
    Are you sure the indirect call destroys the pipeline? Have you benchmarked? My understanding was that in the old days on x86 (pre-i686), indirect calls were very bad (I recall them being a good 10-100 times slower on my K6), but nowadays cpus are smarter and can deal with them just fine. So do some testing before you jump to conclusions! – R.. GitHub STOP HELPING ICE Aug 12 '10 at 13:17
  • @R..: You're right: if I benchmark this on a real CPU, it doesn't make any difference. I'm running my code in qemu, however, and it seems to make a difference there (around 20% more cycles/call). – mtvec Aug 12 '10 at 14:13
  • Then I would just stick with the way you're doing it, with the indirect call. This will allow gcc to generate the correct code for PIC/PIE libraries/executables without you having to insert special hacks to handle these things. – R.. GitHub STOP HELPING ICE Aug 12 '10 at 14:23
  • @R..: Yes that would probably be the best idea. Although I don't have to worry about PIC/PIE (this is kernel code) so I'm still very much interested in finding a good solution for this problem. – mtvec Aug 12 '10 at 14:49
  • Well if it's kernel code, just hard-code the call and put `__attribute__((used))` on the function so it doesn't get optimized out. You don't have to worry about portability if you have a single target OS and cpu architecture. By the way, are you really using C++ in kernel code?? – R.. GitHub STOP HELPING ICE Aug 12 '10 at 14:57
  • @R..: Yes that's probably the best option although I don't like having to hard-code the mangled name... And yes, I really am using C++:-) It's just a hobby kernel, though. – mtvec Aug 12 '10 at 15:26

4 Answers4

18

I got the answer from GCC's mailing list:

asm("call %P0" : : "i"(callee));  // FIXME: missing clobbers

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.


For this to be safe, you need to tell the compiler about all registers that the function call might modify, e.g. : "eax", "ecx", "edx", "xmm0", "xmm1", ..., "st(0)", "st(1)", ....

See Calling printf in extended inline ASM for a full x86-64 example of correctly and safely making a function call from inline asm.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
mtvec
  • 17,846
  • 5
  • 52
  • 83
  • 1
    See also https://stackoverflow.com/questions/37639993/is-this-assembly-function-call-safe-complete for **the dangers of using `call` from inline asm**. It's basically impossible to do reliably for x86-64 System V (Linux), because you can't tell the compiler you want to clobber the red-zone. It's generally a bad idea. See https://stackoverflow.com/questions/7984209/calling-a-function-in-gcc-inline-assembly. – Peter Cordes Sep 01 '17 at 02:59
  • @PeterCordes this seems to be the most upvoted question for "how to do function call from inline assembly", I recommend that we centralize the pitfall answer here. – Ciro Santilli OurBigBook.com Jul 03 '19 at 08:36
  • 1
    @CiroSantilli新疆改造中心996ICU六四事件: Maybe an edit to this answer to include a big fat warning and links to more details would be appropriate. And to include a `"memory"` clobber unless the function you're calling is known not to have any side-effects visible to this function. – Peter Cordes Jul 03 '19 at 08:44
  • 1
    @PeterCordes that would help. My favorite answer so far is Michael's concrete attempt at it: https://stackoverflow.com/questions/37502841/calling-printf-in-extended-inline-asm/37503773#37503773 – Ciro Santilli OurBigBook.com Jul 03 '19 at 08:45
  • 1
    And BTW, update: `%P` is documented now, to print symbol names without decoration. Or to print `foo@plt` if needed/appropriate. https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#x86-Operand-Modifiers – Peter Cordes Jul 03 '19 at 08:45
  • @PeterCordes, any idea on why this doesn't work with `-fpic` (https://gcc.godbolt.org/z/9qd8Y9Ge7)? – Petr Skocik Apr 08 '22 at 11:35
  • 1
    @PSkocik - Possibly because a symbol address isn't an absolute constant in position-independent / relocatable code? No, they work with `-fpie`, and the only(?) difference between PIC and PIE is supporting symbol-interposition. (And that libraries should be using the main executable's definition of global vars, not their own, if the visibility isn't "hidden", but that's not relevant here. And I think that's pretty much a special case of symbol interposition.) IDK why it doesn't just reference the PLT entry since you didn't use `-fno-plt`. – Peter Cordes Apr 08 '22 at 12:28
  • @PSkocik: Ah, using `"g"(callee)`, we can see what GCC wanted to use as the operand: https://gcc.godbolt.org/z/33zos3nvv `call QWORD PTR callee@GOTPCREL[rip]` for `-fpic` / `-fpie`. It insists on take the function address this way, instead of just printing the label and letting the linker replace it. This is normally a good thing when compiling code that sets function pointers to library functions; they bypass the PLT. I think older GCC didn't used to do that, at least when the documentation was written. – Peter Cordes Apr 08 '22 at 12:50
2

Maybe I am missing something here, but

extern "C" void callee(void) 
{

}

void caller(void)
{
  asm("call callee\n");
}

should work fine. You need extern "C" so that the name won't be decorated based on C++ naming mangling rules.

  • This is extremely unsafe. Don't use GNU C basic asm inside a function (unless it's `__attribute__((naked))`). Your `caller` can inline into other functions, but its asm statement doesn't declare any clobbers even though it trashes all of the call-clobbered registers (including ZMM0-31, k0-7, mm0-7, st0-7), and the red-zone. It's a huge pain to safely make a function call from inside an `asm` statement: [Calling printf in extended inline ASM](https://stackoverflow.com/q/37502841) – Peter Cordes Apr 08 '22 at 12:33
1

If you're generating 32-bit code (e.g. -m32 gcc option), the following asm inline emits a direct call:

asm ("call %0" :: "m" (callee));
-1

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.

Community
  • 1
  • 1
nategoose
  • 12,054
  • 27
  • 42
  • 1
    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**. – Nordic Mainframe Aug 12 '10 at 15:53
  • 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 Aug 12 '10 at 16:22
  • It's also GNU C Basic asm, missing clobber declarations on all the call-clobbered registers. So you can expect this to break if used anywhere except a trivial function that doesn't inline, and even then it still might. https://gcc.gnu.org/wiki/ConvertBasicAsmToExtended Only an `__attribute__((naked))` function would be safe (also from the clobbers, because they you're on your own.) – Peter Cordes Feb 03 '23 at 05:33