Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>You can only find out about that at runtime, after the dynamic linker <em>resolves</em> the actual load address.<br> Warning: What follows is slightly deeper magic ...</p> <p>To illustrate what's happening use a debugger:</p> <pre><code>#include &lt;stdio.h&gt; int main(int argc, char **argv) { printf("Hello, World!\n"); return 0; } </code></pre> <p>Compile it (<code>gcc -O8 ...</code>). <code>objdump -d</code> on the binary shows (the optimization of <code>printf()</code> being substituted with <code>puts()</code> for a plain string not withstanding ...):</p> <pre>Disassembly of section .init: [ ... ] Disassembly of section .plt: 0000000000400408 &lt;<b>__libc_start_main@plt-0x10</b>&gt;: <i>400408</i>: ff 35 a2 04 10 00 pushq 1049762(%rip) # 5008b0 <b>&lt;_GLOBAL_OFFSET_TABLE_+0x8&gt;</b>> 40040e: ff 25 a4 04 10 00 jmpq *1049764(%rip) # 5008b8 <b>&lt;_GLOBAL_OFFSET_TABLE_+0x10&gt;</b> [ ... ] 0000000000400428 <b>&lt;puts@plt&gt;</b>: 400428: ff 25 9a 04 10 00 jmpq *1049754(%rip) # 5008c8 &lt;_GLOBAL_OFFSET_TABLE_+0x20&gt; 40042e: 68 01 00 00 00 pushq $0x1 400433: e9 d0 ff ff ff jmpq <i>400408</i> <b>&lt;_init+0x18&gt;</b> [ ... ] 0000000000400500 <b>&lt;main&gt;</b>: 400500: 48 83 ec 08 sub $0x8,%rsp 400504: bf 0c 06 40 00 mov $0x40060c,%edi 400509: e8 1a ff ff ff callq 400428 <b>&lt;puts@plt&gt;</b> 40050e: 31 c0 xor %eax,%eax 400510: 48 83 c4 08 add $0x8,%rsp 400514: c3 retq </pre> <p>Now load it into <code>gdb</code>. Then:</p> <pre> $ gdb ./tcc GNU gdb Red Hat Linux (6.3.0.0-0.30.1rh) [ ... ] (gdb) x/3i 0x400428 0x400428: jmpq *1049754(%rip) # 0x5008c8 &lt;_GLOBAL_OFFSET_TABLE_+32&gt; 0x40042e: pushq $0x1 0x400433: jmpq 0x400408 (gdb) x/gx 0x5008c8 0x5008c8 &lt;_GLOBAL_OFFSET_TABLE_+32&gt;: 0x000000000040042e </pre> <p>Notice this value points back to the instruction directly following the first <code>jmpq</code>; this means the <code>puts@plt</code> slot, on first invocation, will simply "fall through" to:</p> <pre> (gdb) x/3i 0x400408 0x400408: pushq 1049762(%rip) # 0x5008b0 &lt;_GLOBAL_OFFSET_TABLE_+8&gt; 0x40040e: jmpq *1049764(%rip) # 0x5008b8 &lt;_GLOBAL_OFFSET_TABLE_+16&gt; 0x400414: nop (gdb) x/gx 0x5008b0 0x5008b0 &lt;_GLOBAL_OFFSET_TABLE_+8&gt;: 0x0000000000000000 (gdb) x/gx 0x5008b8 0x5008b8 &lt;_GLOBAL_OFFSET_TABLE_+16&gt;: 0x0000000000000000 </pre> <p>The function address and argument aren't initialized yet.<br> This is the state just after program load, but before executing. Now start executing it:</p> <pre>(gdb) break main Breakpoint 1 at 0x400500 (gdb) run Starting program: tcc (no debugging symbols found) (no debugging symbols found) Breakpoint 1, 0x0000000000400500 in main () (gdb) x/i 0x400428 0x400428: jmpq *1049754(%rip) # 0x5008c8 &lt;_GLOBAL_OFFSET_TABLE_+32&gt; (gdb) x/gx 0x5008c8 0x5008c8 &lt;_GLOBAL_OFFSET_TABLE_+32&gt;: 0x000000000040042e </pre> <p>So this hasn't changed <em>yet</em> - but the <em>targets</em> (the <code>GOT</code> contents for the <code>libc</code> initialization) are different now:</p> <pre> (gdb) x/gx 0x5008b0 0x5008b0 &lt;_GLOBAL_OFFSET_TABLE_+8&gt;: 0x0000002a9566b9a8 (gdb) x/gx 0x5008b8 0x5008b8 &lt;_GLOBAL_OFFSET_TABLE_+16&gt;: 0x0000002a955609f0 (gdb) disas 0x0000002a955609f0 Dump of assembler code for function _dl_runtime_resolve: 0x0000002a955609f0 <b>&lt;_dl_runtime_resolve+0&gt;</b>: sub $0x38,%rsp [ ... ] </pre> <p>I.e. at program load time, the dynamic linker will resolve the "<code>init</code>" parts first. It substitutes the <code>GOT</code> references with pointers that redirect into the dynamic linking code. </p> <p>Therefore, when first calling an external-to-the-binary function through the <code>.plt</code> reference, it'll jump into the linker again. Let it do that, then inspect the program after that - the state has changed again:</p> <pre> (gdb) break *0x0000000000400514 Breakpoint 2 at 0x400514 (gdb) continue Continuing. Hello, World! Breakpoint 2, 0x0000000000400514 in main () (gdb) x/i 0x400428 0x400428: jmpq *1049754(%rip) # 0x5008c8 &lt;_GLOBAL_OFFSET_TABLE_+32&gt; (gdb) x/gx 0x5008c8 0x5008c8 : 0x0000002a956c8870 (gdb) disas 0x0000002a956c8870 Dump of assembler code for function <b>puts</b>: 0x0000002a956c8870 &lt;puts+0&gt;: mov %rbx,0xffffffffffffffe0(%rsp) [ ... ] </pre> <p>So there's your redirect right into <code>libc</code> now - the <code>PLT</code> reference to <code>puts()</code> finally got resolved.</p> <p>The instructions to the linker <em>where to insert the actual function load addresses</em> (that we've seen it do for <code>_dl_runtime_resolve</code> comes from special sections in the ELF binary:</p> <pre>$ readelf -a tcc [ ... ] Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flags Align [ ... ] INTERP 0x0000000000000200 0x0000000000400200 0x0000000000400200 0x000000000000001c 0x000000000000001c R 1 [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2] [ ... ] Dynamic section at offset 0x700 contains 21 entries: Tag Type Name/Value 0x0000000000000001 (NEEDED) Shared library: [libc.so.6] [ ... ] Relocation section '.rela.plt' at offset 0x3c0 contains 2 entries: Offset Info Type Sym. Value Sym. Name + Addend 0000005008c0 000100000007 R_X86_64_JUMP_SLO 0000000000000000 __libc_start_main + 0 0000005008c8 000200000007 R_X86_64_JUMP_SLO 0000000000000000 puts + 0 </pre> <p>There's more to ELF than just the above, but these three pieces tell the kernel's binary format handler "this ELF binary has an <em>interpreter</em>" (which is the dynamic linker) that needs to be loaded / initialized first, that it <em>requires</em> <code>libc.so.6</code>, and that offsets <code>0x5008c0</code> and <code>0x5008c8</code> in the program's writeable data section must be <em>substituted</em> by the load addresses for <code>__libc_start_main</code> and <code>puts</code>, respectively, when the step of dynamic linking is actually performed.</p> <p>How exactly that happens, from ELF's point of view, is up to the details of the <em>interpreter</em> (aka, the dynamic linker implementation).</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

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