Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>It's a way to get code fixups (adjusting addresses based on where the code sits in virtual memory) without having to maintain a separate copy of the code for each process. The PLT is the procedure linkage table, one of the structures which makes dynamic loading and linking easier to use.</p> <p><code>printf@plt</code> is actually a small stub which (eventually) calls the real <code>printf</code> function.</p> <p>This real function may be mapped into <em>any</em> location in a given process (virtual address space) as can the code that calls it.</p> <p>So, in order to allow proper code sharing of calling code (left side below), you don't want to apply any fixups to it directly since that will restrict where it can be located in <em>other</em> processes.</p> <p>The <code>PLT</code> is a smaller <em>process-specific</em> area at a reliably-calculated-at-runtime address that <em>isn't</em> shared between processes so any given process can change it however it wants to.</p> <p>In other words, examine the following diagram which shows both your code and the library code mapped to different virtual addresses in two processes:</p> <pre><code> Mapped to: 0x1234 0x9000 0x8888 +-----------------+ +----------+ +----------+ | | | Private | | | ProcA | | | PLT/GOT | | | | | | area | | | | Shared | +----------+ | Shared | ========| application |==============| library |== | code | +----------+ | code | | | | Private | | | ProcB | | | PLT/GOT | | | | | | area | | | +-----------------+ +----------+ +----------+ Mapped to: 0x2020 0x9000 0x6666 </code></pre> <p>This particular example shows a simple case where the PLT maps to a fixed location. In your scenario, it's located relative to the current program counter as evidenced by your program-counter-relative lookup:</p> <pre><code>&lt;printf@plt+0&gt;: jmpq *0x2004c2(%rip) ; 0x600860 &lt;_GOT_+24&gt; </code></pre> <p>A good article can be found <a href="http://dustin.schultz.io/blog/2010/10/02/how-is-glibc-loaded-at-runtime/" rel="noreferrer">here</a>, detailing how <code>glibc</code> is loaded at run time.</p> <p>Basically, the original way in which shared code was made meant it they had to be loaded at the same memory location in the virtual address space of every process that used it. Either that or it couldn't be shared, since the act of fixing up the <em>single</em> shared copy for one process would totally stuff up another process where it was mapped to a different location.</p> <p>By using position independent code, along with the PLT and a global offset table (GOT), the <em>first</em> call to a function <code>printf@plt</code> (in the PLT) is a multi-stage operation, in which:</p> <ul> <li>you call <code>printf@plt</code> in the PLT.</li> <li>it calls the GOT version (via pointer) which <em>initially</em> points back to some set-up code in the PLT.</li> <li>this set-up code loads the relevant shared library if not yet done, then <em>modifies</em> the GOT so that subsequent calls directly to the real <code>printf</code> rather than the set-up code.</li> </ul> <p>On subsequent calls, because the GOT has been modified, the multi-stage approach is simplified:</p> <ul> <li>you call <code>printf@plt</code> in the PLT.</li> <li>it calls the GOT version (via pointer) which points to the <em>real</em> <code>printf</code>.</li> </ul>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload