I’ve been reading a lot about the semantics of load-time linking of shared libraries and one thing that I’m having trouble understanding is how can the main program make references to functions defined in shared libraries? For example, say I have this code
myShared.sh
int get(){ return 0; }
main.c
extern int get(); int main(){ int a = get(); }
I understand that, since shared libraries cannot make any assertions about where they will be placed, they must use the GOT and PLT to make references to their own functions and global data. But how does the actual program that uses said libraries know where the functions will be loaded so that it can make references to them? Obviously, the linker has no idea since the linking of such libraries doesn’t happen until load-time. So, there are only two ways I can think of that would make it possible to reference such external functions.
The linker simply places some place holders where
get
(in the above example) is being called and then adds some meta-data necessary for the loader to then come and replace the place holder with the actual address of the function (like how shared libraries used Load-time relocation before PIC) But this cause notable overhead and was the very motivation (I think) for the introduction of PIC in the first placeThe main program also has its own GOT and PLT and the loader will have to also fill the GOT of the main program along that of the shared library’s (either all at once during loading in case of global variables, or in a lazy fashion using PLT for functions) But this sound like a major duplication of effort.
So, which one of these two, if any, is the method used to resolve shared libraries’ external symbols?
Advertisement
Answer
Symbol resolution in main program is not much different from that in shared libraries and is achieved via main program’s GOT and PLT tables. You are correct that this incurs certain amount of duplication between static (ld
) and dynamic (ld.so
) linkers and slows down program startup but on the other hand allows for increased flexibility at runtime (e.g. interposing symbols via LD_PRELOAD
).