I've seen a lot of questions at SO asking why not all code is compiled as PIC or why we can't always use -fPIC.
However all of the answers lack an explanation about what happens when your objects are compiled with -fPIC but you link them to an executable that is not a PIE (position-independent executable).
From my understanding (using a few small examples and disassembling/inspecting them with readelf), it looks like compiling with -fPIC does not result in a different binary when linked without -pie -fPIE.
My (simplistic?) explanation would be that during link time, it is known that the final executable is not intended to be relocatable, so we can resolve all addresses as in a non-PIC build and get rid of GOT and PLT completely. That is also my observation: If I build a PIE, readelf displays a GOT/PLT section. If I don't build a PIE, the GOT/PLT is gone, no matter if I used -fPIC or not.
My question is whether this observation
- is correct (as well as my explanation) and
- if not, why is my reasoning wrong then?
I found it surprisingly difficult to find a concrete answer to this simple question, that's why I'm asking here.
It is trivial to prove that this is not the case.
Using
gcc (GCC) 12.3.1 20230508 (Red Hat 12.3.1-1)andGNU ld version 2.38-27.fc37Disassembly for
x1:For
x2:That is not an identical binary -- using
-fPICadded some overhead.The amount of overhead depends on the platform, and may also depend on the amount of linker optimizations which can be performed.
Update:
Your reasoning is not wrong, but it requires linker to perform non-trivial amount of work rewriting GOT-relative instructions and relocations into absolute form. You can read more about linker relaxation here.
Whether the linker can optimize all of the GOT-relative instructions out ... depends on how smart the linker is, and how hard it tries.