This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: What is R_X86_64_GOTPLT64 used for?


On Mon, Nov 17, 2014 at 6:14 AM, Michael Matz <matz@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
<foo@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.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]