For testing purposes, I modified the PLT stub that is generated by the llvm linker, lld.
The stub before was:
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *got(%rip)
0x68, 0x00, 0x00, 0x00, 0x00, // pushq <relocation index>
0xe9, 0x00, 0x00, 0x00, 0x00 // jmpq plt[0]
Linking a program with this (original) stub and inspecting it with objdump
yields something like this:
00000000002012d0 <printf@plt>:
2012d0: ff 25 62 0d 00 00 jmpq *0xd62(%rip) # 202038 <__TMC_END__+0x28>
2012d6: 68 02 00 00 00 pushq $0x2
2012db: e9 c0 ff ff ff jmpq 2012a0 <_fini+0x10>
I modified the PLT stub by simply adding a NOP at the end:
0xff, 0x25, 0x00, 0x00, 0x00, 0x00, // jmpq *got(%rip)
0x68, 0x00, 0x00, 0x00, 0x00, // pushq <relocation index>
0xe9, 0x00, 0x00, 0x00, 0x00, // jmpq plt[0]
0x0f, 0x1f, 0x40, 0x00 // nop
I made sure to modify the PltEntrySize
variable so that it reflects the change in size. Linking and running programs with this modification seems to work just fine.
However, when I try to inspect the disassembly of a linked program with objdump
, I see something strange:
00000000002012d0 <printf@plt>:
2012d0: cc int3
2012d1: ff (bad)
2012d2: ff (bad)
2012d3: ff 0f decl (%rdi)
2012d5: 1f (bad)
2012d6: 40 00 ff add %dil,%dil
2012d9: 25 5a 0d 00 00 and $0xd5a,%eax
2012de: 68 02 00 00 00 pushq $0x2
2012e3: e9 b8 ff ff ff jmpq 2012a0 <_fini+0x10>
2012e8: 0f 1f 40 00 nopl 0x0(%rax)
The PLT stub's address is interpreted by objdump
to be at 0x2012d0
, but the real printf@plt
address is at 0x2012d8
! This is confirmed by readelf -s
:
Symbol table '.dynsym' contains 7 entries:
Num: Value Size Type Bind Vis Ndx Name
...
6: 00000000002012d8 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
Where does objdump
get its information from? It could very well be that I forgot to modify something in the linker.