Search code examples
c++linkerlanguage-lawyer

Adding inline to extern "C" function causes linker error when calling from inline assembly


I have an extern "C" function that is supposed to be called from inline assembly but I noticed that when trying to add inline to it (to keep the definition in header file) it causes linker errors. For example, this causes "undefined reference to `f'" (both gcc and clang):

extern "C" inline void f()
{

}

void  __attribute__((naked)) naked_function()
{
    asm volatile("call f");
}

int main()
{
    naked_function();
}

Link to godbolt.

Removing inline will make it work, but I am wondering why it happens.


Solution

  • Since you tagged this language-lawyer:

    According to the C++ standard your program is ill-formed, because asm volatile("call f"); is not valid syntax. A conforming compiler must emit a diagnostic for that.

    Not only is the meaning of an asm statement completely implementation-defined, but the only syntax valid according to the standard is asm(/*tokens*/);, optionally prefixed with an attribute sequence.

    Also, because you use an identifier containing double underscores the program is IFNDR (ill-formed, no diagnostic required) as these identifiers are reserved in all scopes. Technically this has precedent over the ill-formedness that requires a diagnostic, so a compiler does not need to provide a diagnostic for your program as long as the use of __attribute__ is in there to be conforming.

    Obviously both syntax constructs are however intended by the compiler writer for use. What the meaning is supposed to be would be completely up to the compiler. There is no standard/specification governing this. It is reasonable to expect that the compiler will not keep track of specific function calls in an asm statement and therefore will not take these calls into account when deciding which functions need to be instantiated and emitted.

    Also, extern "C" inline, while valid in C++, probably isn't a good idea when actually interfacing with C, because C++ and C standardized on different meanings for inline. I wouldn't expect this to work reliably as intended, even with normal calls to the function.