Since programs are usually dynamically compiled, shared libraries are loaded into process memory upon by ld.so. However, we won’t know the exact location of library functions at compile-time, so for external function calls, the compiler actually makes the program jump to the corresponding entry in the process linkage table, which in turn find the actual addres in the global offset table. The global offset table stores the absolute addresses of symbols from loaded shared libraries; specifically, .got stores the addresses of data symbols and .got.plt stores the addresses of function symbols.

Upon program startup, the GOT will be unfilled since symbol addresses uses lazy binding by default, unless a function address is directly referenced in the code, in which case the address is resolved at load time. Each unfilled GOT entry actually redirects to the top of PLT, which populates the corresponding entry in GOT. However, if program is compiled with full RELRO, GOT is loaded with all relocations before program starts, making the table readonly.