What do linkers actually do with multiply-defined `inline` functions?

孤街醉人 提交于 2019-12-19 17:36:33

问题


In both C and C++, inline functions with external linkage can of course have multiple definitions available at link-time, the assumption being that these definitions are all (hopefully) identical. (I am of course referring to functions declared with the inline linkage specification, not to functions that the compiler or link-time-optimizer actually inlines.)

So what do common linkers typically do when they encounter multiple definitions of a function? In particular:

  • Are all definitions included in the final executable or shared-library?
  • Do all invocations of the function link against the same definition?
  • Are the answers to the above questions required by one or more of the C and C++ ISO standards, and if not, do most common platforms do the same thing?

P.S. Yes, I know C and C++ are separate languages, but they both support inline, and their compiler-output can typically be linked by the same linker (e.g. GCC's ld), so I believe there cannot be any difference between them in this aspect.


回答1:


The linker just has to figure out how to deduplicate all the definitions. That is of course provided that any function definitions have been emitted at all; inline functions may well be inlined. But should you take the address of an inline function with external linkage, you always get the same address (cf. [dcl.fct.spec]/4).

Inline functions aren't the only construction which require linker support; templates are another, as are inline variables (in C++17).




回答2:


If the function is, in fact, inlined, then there's nothing to link. It's only when, for whatever reason, the compiler decides not to expand the function inline that it has to generate an out-of-line version of the function. If the compiler generates an out-of-line version of the function for more than one translation unit you end up with more than one object file having definitions for the same "inline" function.

The out-of-line definition gets compiled into the object file, and it's marked so that the linker won't complain if there is more than one definition of that name. If there is more than one, the linker simply picks one. Usually the first one it saw, but that's not required, and if the definitions are all the same, it doesn't matter. And that's why it's undefined behavior to have two or more different definitions of the same inline function: there's no rule for which one to pick. Anything can happen.




回答3:


inline or no inline, C does not permit multiple external definitions of the same name among the translation units contributing to the same program or library. Furthermore, it does not permit multiple definitions of the same name in the same translation unit, whether internal, external, or inline. Therefore, there can be at most two available definitions of a given function in scope in any given translation unit: one internal and/or inline, and one external.

C 2011, 6.7.4/7 has this to say:

Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern , then the definition in that translation unit is an inline definition . An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

(Emphasis added.)

In specific answer to your questions, then, as they pertain to C:

Are all definitions included in the final executable or shared-library?

Inline definitions are not external definitions. They may or may not be included as actual functions, as inlined code, both, or neither, depending on the foibles of the compiler and linker and on details of their usage. They are not in any case callable by name by functions from different translation units, so whether they should be considered "included" is a bit of an abstract question.

Do all invocations of the function link against the same definition?

C does not specify, but it allows for the answer to be "no", even for different calls within the same translation unit. Moreover, inline functions are not external, so no inline function defined in one translation unit is ever called (directly) by a function defined in a different translation unit.

Are the answers to the above questions required by one or more of the C and C++ ISO standards, and if not, do most common platforms do the same thing?

My answers are based on the current C standard to the extent that it addresses the questions, but as you will have seen, those answers are not entirely prescriptive. Moreover, the standard does not directly address any question of object code or linking, so you may have noticed that my answers are not, for the most part, couched in those terms.

In any case, it is not safe to assume that any given C system is consistent even with itself in these regards for different functions or in different contexts. Under some circumstances it may inline every call to an internal or inline function, so that that function does not appear as a separate function at all. At other times it may indeed emit a function with internal linkage, but that does not prevent it from inlining some calls to that function anyway. In any case, internal functions are not eligible to be linked to functions from other translation units, so the linker is not necessarily involved with linking them at all.




回答4:


I think the correct answer to your question is "it depends".

Consider following pieces of code:

File x.c (or x.cc):

#include <stdio.h>

void otherfunction(void);

inline void inlinefunction(void) {
    printf("inline 1\n");
}

int main(void) {
    inlinefunction();
    otherfunction();
    return 0;
}

File y.c (or y.cc)

#include <stdio.h>

inline void inlinefunction(void) {
    printf("inline 2\n");
}

void otherfunction(void) {
    printf("otherfunction\n");
    inlinefunction();
}

As inline keyword is only a "suggestion" for the compile to inline the function different compilers with different flags behave differently. E.g. looks like C compiler always "exports" inline functions and does not allow for multiple definitions:

$ gcc x.c y.c && ./a.out 
/tmp/ccy5GYHp.o: In function `inlinefunction':
y.c:(.text+0x0): multiple definition of `inlinefunction'
/tmp/ccQkn7m4.o:x.c:(.text+0x0): first defined here
collect2: ld returned 1 exit status

while C++ allows it:

$ g++ x.cc y.cc && ./a.out 
inline 1
otherfunction
inline 1

More interesting - let's try to switch order of files (and so - switch the order of linking):

$ g++ y.cc x.cc && ./a.out 
inline 2
otherfunction
inline 2

Well... it looks that first one counts! But... let's add some optimization flags:

$ g++ y.cc x.cc -O1 && ./a.out 
inline 1
otherfunction
inline 2

And that's the behavior we'd expect. Function got inlined. Different order of files changes nothing:

$ g++ x.cc y.cc -O1 && ./a.out 
inline 1
otherfunction
inline 2

Next we can extend our x.c (x.cc) source with prototype of void anotherfunction(void) and call it in our main function. Let's place anotherfunction definition in z.c (z.cc) file:

#include <stdio.h>

void inlinefunction(void);

void anotherfunction(void) {
    printf("anotherfunction\n");
    inlinefunction();
}

We do not define the body of inlinefunction this time. Compilation/execution for c++ gives following results:

$ g++ x.cc y.cc z.cc && ./a.out 
inline 1
otherfunction
inline 1
anotherfunction
inline 1

Different order:

$ g++ y.cc x.cc z.cc && ./a.out 
inline 2
otherfunction
inline 2
anotherfunction
inline 2

Optimization:

$ g++ x.cc y.cc z.cc -O1 && ./a.out 
/tmp/ccbDnQqX.o: In function `anotherfunction()':
z.cc:(.text+0xf): undefined reference to `inlinefunction()'
collect2: ld returned 1 exit status

So conclusion is: the best is to declare inline together with static, which narrows the scope of the function usage, because "exporting" the function which we'd like to be used inline makes no sense.



来源:https://stackoverflow.com/questions/35233468/what-do-linkers-actually-do-with-multiply-defined-inline-functions

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!