I have this foo.cpp
:
int add(int a, int b) {
return a + b;
}
int add_wrapper(int a, int b) {
return add(a,b);
}
Compiling like so
g++ -c -S -fPIC -O4 -finline-functions foo.cpp -o foo.s
Gives the (demangled) assembly:
.globl add(int, int)
.type add(int, int), @function
add(int, int):
.LFB0:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE0:
.size add(int, int), .-add(int, int)
.p2align 4
.globl add_wrapper(int, int)
.type add_wrapper(int, int), @function
add_wrapper(int, int):
.LFB1:
.cfi_startproc
endbr64
jmp add(int, int)@PLT
.cfi_endproc
.LFE1:
.size add_wrapper(int, int), .-add_wrapper(int, int)
.ident "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0"
Note that add()
is not inlined.
However, without -fPIC
, I get
.globl add(int, int)
.type add(int, int), @function
add(int, int):
.LFB0:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE0:
.size add(int, int), .-add(int, int)
.p2align 4
.globl add_wrapper(int, int)
.type add_wrapper(int, int), @function
add_wrapper(int, int):
.LFB3:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE3:
.size add_wrapper(int, int), .-add_wrapper(int, int)
With add()
inlined.
If I add inline
to the declaration of add()
and compile with -fPIC
, I get
.globl add_wrapper(int, int)
.type add_wrapper(int, int), @function
add_wrapper(int, int):
.LFB1:
.cfi_startproc
endbr64
leal (%rdi,%rsi), %eax
ret
.cfi_endproc
.LFE1:
.size add_wrapper(int, int), .-add_wrapper(int, int)
.ident "GCC: (Ubuntu 11.2.0-19ubuntu1) 11.2.0"
With add()
omitted entirely.
So it seems that inlining is not disallowed by -fPIC
because the compiler will inline if I label the function inline
, but I don't understand why the compiler is unwilling to inline automatically with '-fPIC'.
Does anyone know why this is?
Is there a flag that will convince g++
to inline with -fPIC
but without labeling functions as inline
?
I'm using g++ 11.2, but Compiler Explorer shows that the behavior is consistent across versions: https://godbolt.org/z/Y14edz968.
If you add the always_inline
attribute to add
, you can see the compiler error when it tries to inline:
warning: 'always_inline' function might not be inlinable [-Wattributes]
1 | [[gnu::always_inline]] int add(int a, int b) {
| ^~~
In function 'int add_wrapper(int, int)':
error: inlining failed in call to 'always_inline' 'int add(int, int)': function body can be overwritten at link time
note: called from here
6 | return add(a,b);
| ~~~^~~~~
The add(int, int)
symbol might end up being something else (e.g., another library that was in LD_PRELOAD
with an int add(int, int)
is loaded first, your add_wrapper
should call that one).
You can fix this by making add
invisible ([[gnu::visibility("hidden")]]
) or not giving it external linkage (with an anonymous namespace or static
).
Or you can make g++ assume this will never happen with the switch -fno-semantic-interposition
(which is the default on clang).
See New option in GCC 5.3: -fno-semantic-interposition for more information about semantic interposition.