Position Independent Code
- The promise of shared libraries is to allow multiple processes to share the same library, to accomplish this we need to have the code base be position independent (more below), let's imagine if it was not, how would we implement shared library then?:
- One way is to assign a temporary chunk of virtual space to a fixed shared library, say 0x700000 to 0x300000000 to libc.so for any program. So any executable that runs has those address in it's runtime just reserved for libc.so.
- Now we could have hundreds of libraries with each having it's own space reserved, this means only limited number of libraries can have shared module, plus if some library gets updated we need to update this layout for all processes everywhere which is impossible.
- And there's also massive internal fragmentation and the fact that most processes will just have a lot of unused space in virtual memory that it can't touch and is useless cuz it's obviously not gonna use all those library.
- The above is only required if the code segments were position independent, i.e we were using absolute memory locations while referring to symbols, variables or calling functions, but instead if we made it position independent i.e calling with relative addressing and everything then we will have position independent code!!
- We can direct GNU compilation driver to generate PIC with -fpic flag in gcc, for references to symbols in same Executable Object Files it's already relative by default in modern systems, but for those referencing external functions and variables defined by shared modules, we need some special techniques.
PIC Data References

- Inside an executable, no matter where in memory you move it, the relative distancing between the data segment and the code segment is always the same.
- So to achieve position independence for data references done in code segments that's defined outside of this executable in some shared module, whenever some instruction needs a data like that, it goes and looks it up on it's corresponding GOT (Global Offset Table) entry.
What's a global offset table?
- It's a data structure located on top of data segment. It holds references for all global variables (internal or external) that's called through this executable.
- For internally defined globals, it just hold the relative address pointing to the actual place global is inside the executable which the dynamic linker will do some addition to generate the absolute version at run-time. (GOT does not store absolute address of internally defined global for ASLR stuff i think).
- For externally defined ones, there's section called .rela.dyn (or .rel.dyn for older sys) that like .rel.data section for static linkers, containing instructions for dynamic linker regarding how to do the linking. And after the dynamic linker gets the address it needs, it puts it in the GOT.
- Has 8 bytes address entries.
PIC Function Calls

- There's two different mechanism for PIC Function calls, and it's invoked depending on weather the calling is being done for the first time or if it's a subsequent call due to a mechanism called Lazy Binding.
What and why lazy binding?
- Cuz a executable could have thousands of function and for any instance it's being run, it's more than likely that it will only utilize a small number of them.
- So instead of binding all the functions that this executable might need at once, lazy linking instead resolves a function symbol only after it's been called for the first time. This is lazy binding. - PIC function calls depends on two data structures, the GOT (or .got.plt in modern systems, different from GOT in data reference above) and PLT (Procedure Linkage Table).
Some stuff about PLT:
- It's 16 byte code entires, the first PLT[0] being special one that jumps into dynamic linker
- PLT[1] invokes special system startup function __llibc_start_main
- And everything below that invokes function calls in user code
Some stuff about GOT for PLT:
- Also 8-byte address entires (notice it's CODE for PLT and address entries for GOT)
- GOT[0] and GOT[1] has stuff the dynamic linker might need for resolution purposes, GOT[2] is the entry for dynamic linker in ld-linux
- Subsequent GOT entries corresponds to functions who addresses needs to be resolved at run-time by the dynamic linker. - Step by step of what happens when you call an external function for the first time (refer to diagram above):
- Step 1: Instead of directly calling addvec(), the program calls into PLT[2], the PLT entry for addvec()
- Step 2: PLT then jumps to address specificed in GOT[4] that's supposed to hold the addvec() function but this is lazy binding so GOT does not have it yet so it simply returns back to PLT[2].
- Step 3: Next the function ID (0x1 in our case) is put into the stack , and PLT[2] jumps into PLT[0] which puts GOT[1] in the stack (it has info about relocation procedure for this executable), and then we jump into instructions in address stored by GOT[2] (this is where dynamic linker's starting point is), invoking the dynamic linker.
- The dynamic linker sees the symbol's id, gets info about the executable relocation stuff thr GOT[1] and does relocation by replacing GOT[4] with proper address of addvec()
- Now any subsequent calls to addvec() will result in PLT[2] directly invoking addvec() from GOT[4].