On Mon, Nov 17, 2014 at 6:14 AM, Michael Matz <m...@suse.de> wrote: > Hi, > > On Thu, 13 Nov 2014, H.J. Lu wrote: > >> Linker does: >> >> ... code that looks like it might create just one GOT slot ... >> >> So if a symbol is accessed by both @GOT and @PLTOFF, its >> needs_plt will be true and its got.plt entry will be used for >> both @GOT and @GOTPLT. @GOTPLT has no advantage >> over @GOT, but potentially wastes a PLT entry. > > The above is not correct. Had you tried you'd see this: > > % cat x.c > extern void foo (void); > void main (void) > { > void (*f)(void) = foo; > f(); > foo(); > } > % gcc -fPIE -mcmodel=large -S x.c; cat x.s > ... > movabsq $foo@GOT, %rax > ... > movabsq $foo@PLTOFF, %rax > ... > > So, foo is access via @GOT offset and @PLTOFF. Then, > > % cat y.c > void foo (void) {} > % gcc -o liby.so -shared -fPIC y.c > % gcc -fPIE -mcmodel=large x.s liby.so > % readelf -r a.out > ... > 000000600ff8 000400000006 R_X86_64_GLOB_DAT 0000000000000000 foo + 0 > ... > 000000601028 000400000007 R_X86_64_JUMP_SLO 0000000000000000 foo + 0 > ... > > The first one (to 600ff8) is the normal GOT slot, the second one the GOT > slot for the PLT entry. Both are actually used: > > 00000000004005f0 <foo@plt>: > 4005f0: ff 25 32 0a 20 00 jmpq *0x200a32(%rip) # > 601028 <_GLOBAL_OFFSET_TABLE_+0x28>
They are not: Starting program: /export/home/hjl/bugs/binutils/gotplt/foo Breakpoint 1, main () at main.c:5 5 void (*f)(void) = foo; (gdb) disass Dump of assembler code for function main: 0x000000000040058d <+0>: push %rbp 0x000000000040058e <+1>: mov %rsp,%rbp 0x0000000000400591 <+4>: push %rbx 0x0000000000400592 <+5>: sub $0x18,%rsp 0x0000000000400596 <+9>: lea -0x7(%rip),%rbx # 0x400596 <main+9> 0x000000000040059d <+16>: movabs $0x20042a,%r11 0x00000000004005a7 <+26>: add %r11,%rbx => 0x00000000004005aa <+29>: movabs $0xfffffffffffffff8,%rax 0x00000000004005b4 <+39>: mov (%rbx,%rax,1),%rax 0x00000000004005b8 <+43>: mov %rax,-0x18(%rbp) 0x00000000004005bc <+47>: mov -0x18(%rbp),%rax 0x00000000004005c0 <+51>: callq *%rax 0x00000000004005c2 <+53>: movabs $0xffffffffffdffad0,%rax 0x00000000004005cc <+63>: add %rbx,%rax 0x00000000004005cf <+66>: callq *%rax 0x00000000004005d1 <+68>: mov $0x0,%eax 0x00000000004005d6 <+73>: add $0x18,%rsp 0x00000000004005da <+77>: pop %rbx 0x00000000004005db <+78>: pop %rbp 0x00000000004005dc <+79>: retq End of assembler dump. (gdb) b *0x00000000004005c0 Breakpoint 2 at 0x4005c0: file main.c, line 6. (gdb) b *0x00000000004005cf Breakpoint 3 at 0x4005cf: file main.c, line 7. (gdb) c Continuing. Breakpoint 2, 0x00000000004005c0 in main () at main.c:6 6 f(); (gdb) p $rax $5 = 140737352012384 (gdb) disass $rax Dump of assembler code for function foo: 0x00007ffff7df9260 <+0>: push %rbp 0x00007ffff7df9261 <+1>: mov %rsp,%rbp 0x00007ffff7df9264 <+4>: lea 0x7(%rip),%rdi # 0x7ffff7df9272 0x00007ffff7df926b <+11>: callq 0x7ffff7df9250 <puts@plt> 0x00007ffff7df9270 <+16>: pop %rbp 0x00007ffff7df9271 <+17>: retq End of assembler dump. (gdb) c Continuing. foo Breakpoint 3, 0x00000000004005cf in main () at main.c:7 7 foo(); (gdb) p $rax $6 = 4195472 (gdb) disass $rax Dump of assembler code for function foo@plt: 0x0000000000400490 <+0>: jmpq *0x200552(%rip) # 0x6009e8 <f...@got.plt> 0x0000000000400496 <+6>: pushq $0x2 0x000000000040049b <+11>: jmpq 0x400460 End of assembler dump. (gdb) > That uses the second GOT slot, and: > > 00000000004006ec <main>: > 4006ec: 55 push %rbp > 4006ed: 48 89 e5 mov %rsp,%rbp > 4006f0: 53 push %rbx > 4006f1: 48 83 ec 18 sub $0x18,%rsp > 4006f5: 48 8d 1d f9 ff ff ff lea -0x7(%rip),%rbx # > 4006f5 <main+0x9> > 4006fc: 49 bb 0b 09 20 00 00 movabs $0x20090b,%r11 > 400703: 00 00 00 > 400706: 4c 01 db add %r11,%rbx > 400709: 48 b8 f8 ff ff ff ff movabs $0xfffffffffffffff8,%rax > 400710: ff ff ff > 400713: 48 8b 04 03 mov (%rbx,%rax,1),%rax > > This uses the first slot at 0x600ff8. > > So, no, currently GOT and GOTPLT (at least how it's supposed to be > implemented) are not equivalent. GOT reference: void (*f)(void) = foo; f(); gives you the address of function, foo, in liby.so, without going through PLT, while foo() is called via PLT. For function call, we must use PLT. For pointer reference, we don't use PLT slot: 1. We don't need the indirect branch in PLT. 2. All pointer references to the same function should have the same value. One way to optimize it is to make PLT entry to use the normal GOT slot: jmp *name@GOTPCREL(%rip) 8 byte nop where name@GOTPCREL points to the normal GOT slot updated by R_X86_64_GLOB_DAT relocation at run-time. Should I give it a try? -- H.J.