Lazy loading, .plt / .got / .got.plt

Quick notes on the topic of lazy dynamic loading, learned from "Practical
Binary Analisys" by Denniss Andriesse (Section 2.3.4).

The dynamic linker makes process execution faster by delaying the effective
loading of library functions on their first usage.
This feature can be turned off if real-time predictability is needed.

Invoking a function such as 'puts' from the .text section implies a jump to
the corresponding stub in the .plt (Procedure Linking Table) section.
The .plt section contains read-only executable code.

The first instruction of the stub is an intdirect jump to an address specified
in a dedicated pointer that belongs to the .got.plt (Global Offset Table /
Procedure Linking Table) section.  In C pseudocode:

   void (*ptr)() = got_plt[x];
   ptr();

Initially got_plt[x] is assigned to the instruction that follows the indirect
jump, so the jump effectively results in a no-op on the first execution.
The following instructions will load the parameters for the dynamic loader,
and jump to a lookup procedure internal to the dynamic loader.

The dynamic loader will assign got_plt[x] to the correct (now resolved) address
that the code intended to jump on, before effectively jumpign to it.
Subsequent calls to the same library functions will directly jump to the
resolved library procedure, without invoking the dynamic linker.

Security implications when an exploit can write got_plt[x].

plt -> Executable, read-only, shared among all processes using the library,
       references with indirect jump the .got.plt table of the process.
       Corollary: all processes should map the .got.plt in the same address.

got.plt -> Data, read-write, modified by the dynamic linker.
           Contains function pointers to be updated with the resolved ones.

got -> Data, read-write, similar to .got.plt but involved in *data* rather
       than function pointers.