I'm studying how the GOT and PLT are used in dynamic linking. I'm confused about why each dynamically linked function call seems to jump to a position in the PLT which will always jump to the same position in the GOT. Why not just jump to that position in the GOT in the first place? Why is another layer of indirection necessary?
I probably fundamentally misunderstand something about the GOT and PLT, so here's a brief description of my conceptual understanding of how the PLT and GOT are used.
We have a function called FunctionX, a corresponding position in the PLT called PLT[X], and a corresponding position in the GOT called GOT[X]. The address of the PLT and GOT are known at compile time but the address of FunctionX is not.
In order to call FunctionX:
1) Call (in the assembly sense) address of PLT[X].
2) PLT[X] is a jump to the value contained by GOT[X].
3a) If FunctionX was already resolved, GOT[X] contains the function address so step 2 is a jump to FunctionX.
3b) Otherwise, GOT[X] contains the address of code that will resolve the address of FunctionX at runtime, write that address into GOT[X], and jump to FunctionX. In this case, step 2 causes FunctionX to be resolved and then jumped to.
What is the purpose of step 1?
My understanding of this topic is sketchy so please point out any clarifications that could help the question.