What does @plt mean here?

0x00000000004004b6 <main+30>: callq 0x400398 <printf@plt> 

Somebody knows?

UPDATE

Why do two disas printf give me a different result?

 (gdb) disas printf Dump of assembler code for function printf@plt: 0x0000000000400398 <printf@plt+0>: jmpq *0x2004c2(%rip) # 0x600860 <_GLOBAL_OFFSET_TABLE_+24> 0x000000000040039e <printf@plt+6>: pushq $0x0 0x00000000004003a3 <printf@plt+11>: jmpq 0x400388 (gdb) disas printf Dump of assembler code for function printf: 0x00000037aa44d360 <printf+0>: sub $0xd8,%rsp 0x00000037aa44d367 <printf+7>: mov %rdx,0x30(%rsp) 0x00000037aa44d36c <printf+12>: movzbl %al,%edx 0x00000037aa44d36f <printf+15>: mov %rsi,0x28(%rsp) 0x00000037aa44d374 <printf+20>: lea 0x0(,%rdx,4),%rax 0x00000037aa44d37c <printf+28>: lea 0x3f(%rip),%rdx # 0x37aa44d3c2 <printf+98> 
+70
assembly matplotlib disassembly
Mar 29 2018-11-11T00:
source share
2 answers

This is a way to get code fixes (adjusting addresses depending on the location of the code in virtual memory, which may vary in different processes) without having to maintain a separate copy of the code for each process. PLT is a procedure binding table, one of the structures that simplifies the use of dynamic loading and binding.

printf@plt is actually a small stub that (in the end) calls the real printf function, changing the way you speed up subsequent calls.

The actual printf function can be mapped anywhere in the process (virtual address space), just like the code that tries to call it.

Thus, in order to allow the correct use of the code of the calling code (bottom left) and the called code (bottom right), you do not want to apply any corrections directly to the calling code, as this will limit its location in other processes.

Thus, PLT is a smaller process-specific area, with a reliably calculated address at runtime that is not shared between processes, so any particular process can change it as it sees fit, without negative consequences.




Examine the following diagram, which shows both your code and the library code mapped to different virtual addresses in two different processes, ProcA and ProcB :

 Address: 0x1234 0x9000 0x8888 +-------------+ +---------+ +---------+ | | | Private | | | ProcA | | | PLT/GOT | | | | Shared | +---------+ | Shared | ========| application |=============| library |== | code | +---------+ | code | | | | Private | | | ProcB | | | PLT/GOT | | | +-------------+ +---------+ +---------+ Address: 0x2020 0x9000 0x6666 

This specific example shows a simple case where the PLT is mapped to a fixed location. In your scenario, it is located relative to the current program counter, as evidenced by your search for the program counter:

 <printf@plt+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24> 

I just used fixed addressing to simplify the example.

The original way of sharing code meant that they had to be loaded into the same memory area in each virtual address space of each process that used it. Either this, or it cannot be divided, since the process of repairing one common copy for one process completely fills other processes in which it was mapped to a different location.

Using position-independent code along with PLT and the Global Offset Table (GOT), the first call to printf@plt (in PLT) is a multi-step operation in which the following actions are performed:

  • You call printf@plt in PLT.
  • It calls the GOT version (via a pointer), which initially points to some setup code in the PLT.
  • This installation code loads the appropriate shared library, if not already done, then modifies the GOT pointer so that subsequent calls directly to the real printf and not to the PLT installation code.
  • It then calls the downloaded printf code at the correct address for this process.

In subsequent calls, since the GOT pointer has been changed, the multi-step approach is simplified:

  • You call printf@plt in PLT.
  • It calls the GOT version (via a pointer), which now points to the actual printf .

A good article can be found here detailing how glibc loads at runtime.

+110
Mar 29 '11 at 7:40
source share

Not sure, but probably what you saw makes sense. The first time you run the disas command, printf is not yet called, so it is not allowed. When your program calls the printf method on the first update of GOT, and now printf is resolved, and GOT points to a real function. So the next call to the disas command shows the actual printf assembly.

+4
Sep 04
source share



All Articles