Direct C function call using GCC's inline assembly

14,081

Solution 1

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.

Solution 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.

Share:
14,081

Related videos on Youtube

mtvec
Author by

mtvec

Updated on April 30, 2022

Comments

  • mtvec
    mtvec about 2 years

    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.

    • R.. GitHub STOP HELPING ICE
      R.. GitHub STOP HELPING ICE almost 14 years
      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!
  • Nordic Mainframe
    Nordic Mainframe almost 14 years
    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.
  • nategoose
    nategoose almost 14 years
    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...
  • Peter Cordes
    Peter Cordes almost 7 years
    See also stackoverflow.com/questions/37639993/… 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 stackoverflow.com/questions/7984209/….
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com almost 5 years
    @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.
  • Peter Cordes
    Peter Cordes almost 5 years
    @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.
  • Ciro Santilli OurBigBook.com
    Ciro Santilli OurBigBook.com almost 5 years
    @PeterCordes that would help. My favorite answer so far is Michael's concrete attempt at it: stackoverflow.com/questions/37502841/…
  • Peter Cordes
    Peter Cordes almost 5 years
    And BTW, update: %P is documented now, to print symbol names without decoration. Or to print foo@plt if needed/appropriate. gcc.gnu.org/onlinedocs/gcc/…
  • Peter Cordes
    Peter Cordes about 2 years
    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