PLT stands for Procedure Linkage Table. It is effectively a table of function addresses. More precisely, as illustrated here, the PLT is a table where each entry consists of a jump instruction to where the code of the function really is. The PLT thus consists of function stubs.
Calls to dynamically linked functions are compiled to calls to the PLT address, rather than the address where the function really is. Thanks to this indirection, external calls in a module can be implemented with a relative jump into the PLT. The dynamic linker updates the PLT with the runtime address of the actual code, which depends on where the called module is located in memory relative to the callee.
The problem solved by this extra level of indirection is dynamic linking where the address of the libraries cannot be predicted at compile time and the dynamic linker does not need to update the code itself (which would preclude sharing between instances of a library that is loaded at different addresses in different processes).
This explains <__libc_start_main@plt>
. That the jump is indicated as into perror@plt
is confusing information from the disassembler. <perror@plt+0x2008e0>
means that the jump target is 0x2008e0 bytes after the start of the PLT entry for perror
— if this was a jump into the code of the perror
function, that would be one huge function! In fact the jump is to the real code of the main
entry point, which happens to be located this many bytes from the perror
PLT entry (if you're using GNU binutils, I think objdump
picks the last PLT entry here). The binary is compiled without debugging symbols, so the code of all functions appears to the disassembler as a huge block of .text
, and the debugger has no better way to name that particular address. If debugging symbols had been present then the disassembler would have extracted the function name from the debugging information in the binary.