PLT and GOT

Dynamic Linking

The Procedure Linkage Table (PLT) and Global Offset Table (GOT) are sections within an Executable and Linkable Format (ELF) file that play a significant role in dynamic linking. The purpose of dynamic linking is to reduce the size of binaries by allowing them to rely on system libraries, such as the C standard library (libc), to provide the majority of their functionality.

For example, an ELF file does not include its own version of the 'puts' function compiled within it. Instead, it dynamically links to the 'printf' function of the system it is running on. In addition to smaller binary sizes, this also means that users can upgrade their libraries without having to download all the binaries again each time a new version is released.

How it works ?

The linking is performed through the cooperation of the Procedure Linkage Table (PLT) and the Global Offset Table (GOT).

When the 'printf' function, for example, is called in C and compiled as an ELF executable, it is not included as 'printf' in the file. Instead, it is compiled as 'printf@plt' :

   0x0804925b <+137>:   push   0x804a012
   0x08049260 <+142>:   call   0x8049040 <printf@plt>
   0x08049265 <+147>:   add    esp,0x10

The program does not know the actual location of 'puts', so it jumps to the 'printf@plt' entry instead. When this occurs, 'printf@plt' performs some specific actions.

  • If there isn't a GOT entry, it will resolve it and jump there.

  • If there is a GOT entry for printf, it jumps to the address stored there.

The GOT is a massive table of addresses; these addresses are the actual locations in memory of the libc functions. printf@got, for example, will contain the address of printf in memory. When the PLT gets called, it reads the GOT address and redirects execution there. If the address is empty, it coordinates with the ld.so (also called the dynamic linker/loader) to get the function address and stores it in the GOT.

  • Calling the PLT address of a function is equivalent to calling the function itself

  • The GOT address contains addresses of functions in libc, and the GOT is within the binary.

Read tables

Some tools permits to read the PLT and GOT tables :

$ readelf -r chall

Relocation section '.rel.dyn' at offset 0x3bc contains 1 entry:
 Offset     Info    Type            Sym.Value  Sym. Name
0804bffc  00000606 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x3c4 contains 9 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
0804c00c  00000107 R_386_JUMP_SLOT   00000000   strcmp@GLIBC_2.0
0804c010  00000207 R_386_JUMP_SLOT   00000000   printf@GLIBC_2.0
0804c014  00000307 R_386_JUMP_SLOT   00000000   fclose@GLIBC_2.1
0804c018  00000407 R_386_JUMP_SLOT   00000000   fread@GLIBC_2.0
0804c01c  00000507 R_386_JUMP_SLOT   00000000   puts@GLIBC_2.0
0804c020  00000707 R_386_JUMP_SLOT   00000000   __libc_start_main@GLIBC_2.0
0804c024  00000807 R_386_JUMP_SLOT   00000000   fopen@GLIBC_2.1
0804c028  00000907 R_386_JUMP_SLOT   00000000   putchar@GLIBC_2.0
0804c02c  00000a07 R_386_JUMP_SLOT   00000000   __isoc99_scanf@GLIBC_2.7
from pwn import *

elf = ELF("./chall")

print(elf.plt)
# {'strcmp': 134516784, 'printf': 134516800, 'fclose': 134516816, 'fread': 134516832, 'puts': 134516848, '__libc_start_main': 134516864, 'fopen': 134516880, 'putchar': 134516896, '__isoc99_scanf': 134516912}

print(elf.got)
# {'__gmon_start__': 134529020, 'strcmp': 134529036, 'printf': 134529040, 'fclose': 134529044, 'fread': 134529048, 'puts': 134529052, '__libc_start_main': 134529056, 'fopen': 134529060, 'putchar': 134529064, '__isoc99_scanf': 134529068}

Resources

Last updated