Bug 69271 - LTO drops weak binding from aliases
Summary: LTO drops weak binding from aliases
Status: ASSIGNED
Alias: None
Product: gcc
Classification: Unclassified
Component: lto (show other bugs)
Version: 5.0
: P3 normal
Target Milestone: ---
Assignee: Jan Hubicka
URL:
Keywords: lto, wrong-code
Depends on:
Blocks:
 
Reported: 2016-01-14 11:33 UTC by nsz
Modified: 2022-05-07 07:52 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2016-01-15 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description nsz 2016-01-14 11:33:08 UTC
i guess it is related to bug 67548
but it happens without -r now.

void foo(void) {}
extern void foo_alias(void) __attribute__((weak, alias("foo")));
int bar = 0;
extern int bar_alias __attribute__((weak, alias("bar")));

compiled as

gcc -shared -fPIC -flto -o foo.so foo.c

$ readelf --dyn-sym -W foo.so |grep foo
     8: 0000000000000640     6 FUNC    GLOBAL DEFAULT   10 foo_alias
     9: 0000000000000640     6 FUNC    GLOBAL DEFAULT   10 foo
$ readelf --dyn-sym -W foo.so |grep bar
     4: 00000000002008cc     4 OBJECT  GLOBAL DEFAULT   21 bar
     6: 00000000002008cc     4 OBJECT  GLOBAL DEFAULT   21 bar_alias

without -flto i get the expected

$ readelf --dyn-sym -W foo.so |grep foo
     8: 0000000000000640     7 FUNC    WEAK   DEFAULT   10 foo_alias
     9: 0000000000000640     7 FUNC    GLOBAL DEFAULT   10 foo
$ readelf --dyn-sym -W foo.so |grep bar
     4: 00000000002008cc     4 OBJECT  GLOBAL DEFAULT   21 bar
     6: 00000000002008cc     4 OBJECT  WEAK   DEFAULT   21 bar_alias

this silently breaks the libc if it's compiled with lto

my gcc version is 6.0.0 20160113, tested on x86_64 and aarch64 targets.
Comment 1 nsz 2016-01-14 12:42:01 UTC
hm it happens on gcc-5 too, gcc-49 is ok.
Comment 2 Richard Biener 2016-01-14 14:50:12 UTC
Well, -shared is somewhat similar here.
Comment 3 Jan Hubicka 2016-01-15 11:19:58 UTC
mine.  I wonder why WEAK makes difference here at all when all symbols are interposable with PIC.
Comment 4 Jan Hubicka 2016-01-15 11:44:04 UTC
The optimization was intentional - dropping the weak bit makes GCC to optimize
the references to symbol better (knowing it won't be NULL because the definition
is provided). I wonder how this break glibc. What is the difference between weak
and non-weak symbols in dynamic linking?
Comment 5 nsz 2016-01-15 12:17:35 UTC
copy pasting from
http://www.openwall.com/lists/musl/2016/01/13/2
(this is musl libc, but glibc has the same issue)

lto breaks symbol binding for environ, _environ, ___environ.
(they should be weak, without that environ in a main binary
has different address than in libc.so)

libc.so built with -flto:
$ readelf --dyn-syms -W libc.so |grep envi
    22: 000000000028eb90     8 OBJECT  GLOBAL DEFAULT   15 __environ
   398: 000000000028eb90     8 OBJECT  GLOBAL PROTECTED   15 ___environ
  1034: 000000000028eb90     8 OBJECT  GLOBAL PROTECTED   15 _environ
  1107: 000000000028eb90     8 OBJECT  GLOBAL DEFAULT   15 environ

libc.so without -flto:
$ readelf --dyn-syms -W libc.so |grep envi
    22: 000000000028d2d8     8 OBJECT  GLOBAL DEFAULT   15 __environ
   398: 000000000028d2d8     8 OBJECT  WEAK   PROTECTED   15 ___environ
  1034: 000000000028d2d8     8 OBJECT  WEAK   PROTECTED   15 _environ
  1107: 000000000028d2d8     8 OBJECT  WEAK   DEFAULT   15 environ
Comment 6 nsz 2016-01-15 14:26:32 UTC
to complete the example here is a test application:

#include <stdio.h>
#include <stdlib.h>
extern char **environ;
int main()
{
	printf("&environ: %p, environ: %p, *environ: %p\n", &environ, environ, *environ);
	clearenv(); // *environ = 0
	putenv("TEST=1"); // should change environ
	printf("&environ: %p, environ: %p, *environ: %p\n", &environ, environ, *environ);
}

with correct libc.so:

$ gcc a.c
$ ./a.out 
&environ: 0x6008b0, environ: 0x7fffb9b0b478, *environ: 0x7fffb9b0d651
&environ: 0x6008b0, environ: 0x600020, *environ: 0x400649
$ readelf --dyn-sym -W ./a.out |grep envi
     2: 00000000006008b0     8 OBJECT  WEAK   DEFAULT   19 _environ
     5: 00000000006008b0     8 OBJECT  GLOBAL DEFAULT   19 __environ
     7: 00000000006008b0     8 OBJECT  WEAK   DEFAULT   19 environ
     8: 00000000006008b0     8 OBJECT  WEAK   DEFAULT   19 ___environ

if libc.so is compiled with -flto:

$ gcc a.c
$ ./a.out 
&environ: 0x600850, environ: 0x7fff52af6158, *environ: 0x7fff52af6651
&environ: 0x600850, environ: 0x7fff52af6158, *environ: 0
$ readelf --dyn-sym -W ./a.out |grep envi
     5: 0000000000600850     8 OBJECT  GLOBAL DEFAULT   19 environ

so environ is shared between a.out and libc.so
in the beginning (clearenv worked), but the
address of the symbol (&environ) is different
so changing it in the libc did not have an effect
in the main module (putenv failed).

this might be an issue in the static or dynamic
linker but the difference is observable.
Comment 7 Rich Felker 2016-01-15 17:55:10 UTC
Jan Hubicka, when ld resolves a reference that requires a copy relocation to a weak definition in a shared library, it searches for a strong symbol for which the weak definition is an alias, and replaces the reference with one to the strong symbol. This is necessary to ensure that &weak_alias == &strong_sym at runtime after the copy relocation changes the address of the symbol.
Comment 8 Jan Hubicka 2016-05-30 12:27:53 UTC
Ok, compiling:
int bar = 0;
extern int bar_alias __attribute__((weak, alias("bar")));
main()
{
  printf ("%i %i\n",bar,bar_alias);
}

as a static library leads to no dynamic relocations, but compiling as shared we get bar_alias still declared weak which affects the dynamic linking (because it can be replaced by strong definition which come later in the linking process).
So all we need to do is to avoid turning WEAK to non-WEAK for non-hidden symbols when building with PIC?