Bug 88954 - __attribute__((noplt)) doesn't work with function pointers
Summary: __attribute__((noplt)) doesn't work with function pointers
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: target (show other bugs)
Version: 9.0
: P3 normal
Target Milestone: 9.0
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on: 67400
Blocks:
  Show dependency treegraph
 
Reported: 2019-01-21 16:49 UTC by H.J. Lu
Modified: 2019-01-23 17:05 UTC (History)
1 user (show)

See Also:
Host:
Target: i386, x86-64
Build:
Known to work:
Known to fail:
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description H.J. Lu 2019-01-21 16:49:13 UTC
[hjl@gnu-cfl-1 noplt-3]$ make clean
rm -f *.o *.so *.s
[hjl@gnu-cfl-1 noplt-3]$ cat main.c 
void f_noplt(void) __attribute__((noplt));
void (*p_noplt)(void) = f_noplt;
void g(void (*)(void));

int main()
{
        g(p_noplt); // lazy: linker sets p_noplt to PLT address
        g(f_noplt); // lazy: linker sets mov immediate to PLT address
        f_noplt();  // non-lazy
        return 0;
}
[hjl@gnu-cfl-1 noplt-3]$ cat lib.c 
__attribute__((noplt)) void f_noplt(void) {}
void g(void p(void)) {p();}
[hjl@gnu-cfl-1 noplt-3]$ make CC=gcc
gcc -O2 -fno-pic    -c -o main.o main.c
gcc -O2 -fno-pic  -fPIC   -c -o lib.o lib.c
gcc  -shared -o lib.so lib.o
gcc -no-pie -o x main.o lib.so -Wl,-R,.
./x
[hjl@gnu-cfl-1 noplt-3]$ readelf -rW x

Relocation section '.rela.dyn' at offset 0x4c8 contains 3 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000403fe8  0000000400000006 R_X86_64_GLOB_DAT      0000000000401030 f_noplt + 0
0000000000403ff0  0000000100000006 R_X86_64_GLOB_DAT      0000000000000000 __libc_start_main@GLIBC_2.2.5 + 0
0000000000403ff8  0000000200000006 R_X86_64_GLOB_DAT      0000000000000000 __gmon_start__ + 0

Relocation section '.rela.plt' at offset 0x510 contains 2 entries:
    Offset             Info             Type               Symbol's Value  Symbol's Name + Addend
0000000000404018  0000000400000007 R_X86_64_JUMP_SLOT     0000000000401030 f_noplt + 0

This dynamic relocation shouldn't be here.

0000000000404020  0000000300000007 R_X86_64_JUMP_SLOT     0000000000000000 g + 0
[hjl@gnu-cfl-1 noplt-3]$
Comment 1 Martin Sebor 2019-01-21 21:46:54 UTC
I'm wondering if noplt is meant to be a property of the function pointer or that of its type?

Either way, what should happen in cases when a noplt pointer is assigned the address of a PLT function?  E.g.,:

  void (*p_noplt)(void) = f_noplt;

  void f (int i)
  {
    if (i < 0)
      p_noplt = f_plt;
  }

Do calls through p_noplt still happen without the use of the PLT after the assignment?  Should a warning be issued when a noplt pointer is assigned the address of an ordinary PLT function?
Comment 2 H.J. Lu 2019-01-21 22:00:43 UTC
(In reply to Martin Sebor from comment #1)
> I'm wondering if noplt is meant to be a property of the function pointer or
> that of its type?
> 
> Either way, what should happen in cases when a noplt pointer is assigned the
> address of a PLT function?  E.g.,:
> 
>   void (*p_noplt)(void) = f_noplt;
> 
>   void f (int i)
>   {
>     if (i < 0)
>       p_noplt = f_plt;
>   }
> 
> Do calls through p_noplt still happen without the use of the PLT after the
> assignment?  Should a warning be issued when a noplt pointer is assigned the
> address of an ordinary PLT function?

noplt attribute only applies to function, not function pointer.
Comment 3 Martin Sebor 2019-01-22 04:03:22 UTC
My question is about the change you are proposing.  How do you expect g() to be called if the test case from comment #0 is modified for example as follows:

  void f_plt(void);
  void f_noplt(void) __attribute__((noplt));
  void (*p_noplt)(void) = f_noplt;
  void g(void (*)(void));

  int main()
  {
    if (getenv ("USE_PLT"))
      p_noplt = f_plt;   // should this be diagnosed?

    g (p_noplt);
  }

In most other cases, initializing a pointer with the address of a function declared with some function attribute (e.g., const, nonnull, noreturn, pure, or warn_unused_result) doesn't transfer the special properties to the pointer, so I'm trying to understand what semantics you are after and if there is any potential for user errors and decide whether they should be detected.

I happen to think it would make sense to make it possible to imbue function pointers with some (but not all) of the same attributes as those that apply to functions, but there certainly are cases where doing so could cause problems, just as there are cases where not doing so can.
Comment 4 H.J. Lu 2019-01-22 04:55:12 UTC
(In reply to Martin Sebor from comment #3)
> My question is about the change you are proposing.  How do you expect g() to
> be called if the test case from comment #0 is modified for example as
> follows:
> 
>   void f_plt(void);
>   void f_noplt(void) __attribute__((noplt));
>   void (*p_noplt)(void) = f_noplt;
>   void g(void (*)(void));
> 
>   int main()
>   {
>     if (getenv ("USE_PLT"))
>       p_noplt = f_plt;   // should this be diagnosed?

f_plt will be handled by

000000404020  000600000007 R_X86_64_JUMP_SLO 0000000000401040 f_plt + 0

>     g (p_noplt);
>   }
> 
> In most other cases, initializing a pointer with the address of a function
> declared with some function attribute (e.g., const, nonnull, noreturn, pure,
> or warn_unused_result) doesn't transfer the special properties to the
> pointer, so I'm trying to understand what semantics you are after and if
> there is any potential for user errors and decide whether they should be
> detected.
> 
> I happen to think it would make sense to make it possible to imbue function
> pointers with some (but not all) of the same attributes as those that apply
> to functions, but there certainly are cases where doing so could cause
> problems, just as there are cases where not doing so can.

__attribute__((noplt)) only applies to functions, which instructors
compiler to avoid PLT when accessing the marked function, via function
pointer nor direct call.  The function pointer itself has no impact
on PLT.
Comment 5 Richard Biener 2019-01-22 11:13:42 UTC
For indirect calls the attributes on the function type pointed to a relevant.  Unioning attributes from the actually called function (if the compiler can
figure that out) can be appropriate depending on the actual attribute.
Comment 6 hjl@gcc.gnu.org 2019-01-22 14:54:15 UTC
Author: hjl
Date: Tue Jan 22 14:53:41 2019
New Revision: 268152

URL: https://gcc.gnu.org/viewcvs?rev=268152&root=gcc&view=rev
Log:
i386: Load external function address via GOT slot

With noplt attribute, we load the external function address via the GOT
slot so that linker won't create an PLT entry for extern function address.

gcc/

	PR target/88954
	* config/i386/i386.c (ix86_force_load_from_GOT_p): Also check
	noplt attribute.

gcc/testsuite/

	PR target/88954
	* gcc.target/i386/pr88954-1.c: New test.
	* gcc.target/i386/pr88954-2.c: Likewise.

Added:
    trunk/gcc/testsuite/gcc.target/i386/pr88954-1.c
    trunk/gcc/testsuite/gcc.target/i386/pr88954-2.c
Modified:
    trunk/gcc/ChangeLog
    trunk/gcc/config/i386/i386.c
    trunk/gcc/testsuite/ChangeLog
Comment 7 nsz 2019-01-23 16:23:24 UTC
note that with

void f_noplt(void) __attribute__((noplt));
void (*p)(void) = f_noplt;

the linker may create a PLT for f_noplt and use its address to initialize p in
case of non-pie linking.

alternatively the linker may emit a dynamic relocation for p so it is filled in
by the dynamic linker to the actual address of f_noplt.

it seems the bfd linker on x86_64 does the latter (if there is otherwise no PLT),
but e.g. the gold linker does the former. (as far as the sysv abi is concerned
both behaviours are correct, the linker does not know about the noplt attr.)

this means that (depending on linker behaviour) a noplt function may get a PLT in
non-pie executables (so noplt can only avoid lazy binding and jump slot relocs
reliably in pic code), may be linkers should be fixed so noplt always avoids PLT
(on x86_64, other targets have other issues with non-pic), but then this has to
be abi to be reliable.
Comment 8 H.J. Lu 2019-01-23 17:05:09 UTC
(In reply to nsz from comment #7)
> note that with
> 
> void f_noplt(void) __attribute__((noplt));
> void (*p)(void) = f_noplt;
> 
> the linker may create a PLT for f_noplt and use its address to initialize p
> in
> case of non-pie linking.
> 
> alternatively the linker may emit a dynamic relocation for p so it is filled
> in
> by the dynamic linker to the actual address of f_noplt.
> 
> it seems the bfd linker on x86_64 does the latter (if there is otherwise no
> PLT),
> but e.g. the gold linker does the former. (as far as the sysv abi is
> concerned
> both behaviours are correct, the linker does not know about the noplt attr.)
> 
> this means that (depending on linker behaviour) a noplt function may get a
> PLT in
> non-pie executables (so noplt can only avoid lazy binding and jump slot
> relocs
> reliably in pic code), may be linkers should be fixed so noplt always avoids
> PLT
> (on x86_64, other targets have other issues with non-pic), but then this has
> to
> be abi to be reliable.

Gold doesn't have the same optimization as bfd linker on x86.  This is just
one of them.