3

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.

Praxeolitic
  • 22,455
  • 16
  • 75
  • 126
  • Just guessing here, but maybe PLT is a standardized thing but the fact that it happens to use the GOT is just an accidental implementation detail. – Jester Jul 27 '17 at 15:31
  • 1
    See https://stackoverflow.com/questions/43048932/why-does-the-plt-exist-in-addition-to-the-got-instead-of-just-using-the-got?rq=1 for why the PLT can't easily be eliminated on x86 targets. – Ross Ridge Jul 27 '17 at 22:43
  • @RossRidge That answers my question. Thanks. – Praxeolitic Jul 27 '17 at 22:57

1 Answers1

2

Just something to think about

Imagine the first call of whatever address is at GOT[X] from some client code (for example main). As you said in 3b) this will call the code that will resolve the address of FunctionX. But how can the resolving code know that you want to resolve FunctionX? You are calling it like a normal function, you do not provide any additional information for the resolver that it is FunctionX address you want to patch. Stub pushes on stack extra information before jumping to GOT[X].

Last few paragraphs of this article helped a lot.

Tom
  • 71
  • 6
  • I left that part out. Essentially, the address that GOT[X] initially contains is the address of code that is specifically for resolving FunctionX. That code sets parameters specifying that we're resolving FunctionX and the address should be written to GOT[X] and then dispatches to the general purpose symbol resolving code. My understanding is that the initial call to PLT[X] is always a simple jump. – Praxeolitic Jul 27 '17 at 16:19
  • Yes, and the code that sets the paremetres is the stub isn't it? That's its purpose as far as I know. – Tom Jul 27 '17 at 16:22
  • The first instruction in PLT[X] is jump back to itself, it is practically NOP instruction on its first call. – Tom Jul 27 '17 at 16:24
  • GOT[X] initially contains address of the second instruction in PLT[X] – Tom Jul 27 '17 at 16:27
  • My understanding is that the code that specifies "resolve FunctionX at GOT[X]" is in the PLT, but it's not at PLT[X]. PLT[X] is always just a jump to the value that GOT[X] contains. I don't understand the purpose of the first visit to the PLT. – Praxeolitic Jul 27 '17 at 16:28
  • Let's make sure we talk of the same PLT[X] ;) For me it contains 3 instructions, jump push jump – Tom Jul 27 '17 at 16:39
  • Sorry, I think the confusion was that I was thinking of it as two parts and using "PLT[X]" to refer to just the initial jump. So yes, it's contents are jump push jump. Agreed! – Praxeolitic Jul 27 '17 at 16:44
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/150339/discussion-between-tom-and-praxeolitic). – Tom Jul 27 '17 at 16:45