Bug 26314 - Linking LTO objects with conflicting symbol definitions from static and shared libraries fails
Summary: Linking LTO objects with conflicting symbol definitions from static and share...
Status: RESOLVED FIXED
Alias: None
Product: binutils
Classification: Unclassified
Component: ld (show other bugs)
Version: 2.35
: P2 normal
Target Milestone: 2.38
Assignee: H.J. Lu
URL: https://sourceware.org/pipermail/binu...
Keywords:
Depends on:
Blocks:
 
Reported: 2020-07-29 12:53 UTC by Nick Clifton
Modified: 2023-01-23 16:33 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
A patch (2.88 KB, patch)
2020-07-29 19:00 UTC, H.J. Lu
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Nick Clifton 2020-07-29 12:53:57 UTC
In simplest terms, if we use LTO to build binutils the resultant binaries will
fault all over the place.  If you look at it in the debugger we'll have jumped to 0x0 via an indirect jump out of the PLT.  The problem is the GOT entries are
zero'd out.

It looks like the preconditions are:

First, we need to have a DSO which provides a symbol definition (libbfd).

Second, we need to have an executable which links against that DSO (ar).  The
components of that executable are LTO objects and in one or more of those LTO'd
objects there must be another definition of the symbol in question (-liberty and
libiberty.a referenced on the command line to link ar).

The when linking the executable the linker has to merge the two definitions.  In
general we'll prefer the version from the executable over the version from the
DSO.

However, in the case of an LTO link, the symbol's input section is marked as
SEC_EXCLUDE for the main executable (that seems to be an artifact of LTO and the
section flags it uses).  In that scenario the output section will be reset to the ABS section.  The net result is we create a dynamic symbol with an absolute type.

When the dynamic linker performs its symbol resolution that absolute dynamic
symbol will take precedence over the symbol in the DSO and the dynamic linker
will slam a new value into the GOT entry for the symbol.

This is fairly abstract, so here is a reproducer in the form of binutils
itself that you can examine in a debugger.  It uses the Fedora rawhide (f33) 
binutils sources:

  1. fedpkg clone binutils

  2. sed -i -e 's/%define _lto_cflags/#define _lto_cflags/' binutils.spec
  [This step disables a workaround currently in the binutils.spec.
  The  workaround disables building the binutils with LTO enabled].

  3. fedpkg srpm
  4. fedpkg mock-config > my.cfg
  5. mock -r my.cfg --without=testsuite *.src.rpm
  6. mock --uniqueext=xxx --dnf -r my.cfg shell
  7. nm --dynamic /builddir/build/BUILD/binutils-2.35/binutils/.libs/ar

  This will show lots of entries, including:

  0000000000000000 A lrealpath

  Attempts to run the ar binary will fail, as will all the other newly
  built binutils tools.

  [I am attempting to create a simpler, stand alone reproducer for this
  problem.  I will add an update to this PR when/if I find one].

  Jeff Law has come up with a hack which appears to fix the problem,
  but we are not sure if it is the correct solution:

diff --git a/bfd/elflink.c b/bfd/elflink.c
index 998b72f228..2f06e835c1 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -1633,7 +1633,8 @@ _bfd_elf_merge_symbol (bfd *abfd,
       && newdef
       && (olddef
          || (h->root.type == bfd_link_hash_common
-             && (newweak || newfunc))))
+             && (newweak || newfunc)))
+      && (oldsec->flags & SEC_EXCLUDE) == 0)
     {
       *override = TRUE;
       newdef = FALSE;

THe effect here is the symbol will be resolved via the libbfd.so DSO.  That
doesn't seem entirely correct since we have a definition of lrealpath in
libiberty, which is referenced twice on the link line.
Comment 1 H.J. Lu 2020-07-29 19:00:22 UTC
Created attachment 12732 [details]
A patch

Please try this.
Comment 2 H.J. Lu 2020-07-29 22:24:05 UTC
A patch is posted at

https://sourceware.org/pipermail/binutils/2020-July/112629.html
Comment 3 Alan Modra 2020-07-30 08:14:16 UTC
I'm having trouble reproducing this problem using my own x86_64-linux gcc-10, and trying to guess the flags used to compile binutils.
Comment 4 H.J. Lu 2020-07-30 11:48:38 UTC
(In reply to Alan Modra from comment #3)
> I'm having trouble reproducing this problem using my own x86_64-linux
> gcc-10, and trying to guess the flags used to compile binutils.

A testcase is on pr96385 branch at:

https://gitlab.com/x86-gcc/gcc-bugs
Comment 5 Alan Modra 2020-07-31 08:06:58 UTC
> https://gitlab.com/x86-gcc/gcc-bugs

Thanks for that.  Here are my observations about the link:
gcc -O2 -g -o ar -Wl,--as-needed arparse.o arlex.o ar.o not-ranlib.o arsup.o rename.o binemul.o emul_vanilla.o bucomm.o version.o filemode.o libbfd-2.35-3.fc33.so libiberty.a -Wl,-R,.

All of the above .o files are lto, leading to libbfd-2.35-3.fc33.so not being found needed when loading the IR objects.  That's problem number one:  We exclude IR references when deciding a shared library is needed.  See PR15146.  Thus none of the libbfd.so symbols are loaded before libiberty.a is scanned, and libbfd.so contains copies of libiberty.a functions.  We ought to be using the libbfd.so copies rather than extracting them from the archive (an object is extracted even to satisfy IR symbols).  After lto recompilation, libbfd.so is of course found to be needed and loaded.  But that causes more problems.  The lto recompilation didn't see symbol references from libbfd.so and variables like _xexit_cleanup are made local in the recompiled objects.  Oops, two copies of them.  Finally, those silly undefined symbols in the lto output debug files, combined with definitions in both libbfd.so and IR objects result in IR symbols being made dynamic.
Comment 6 Sourceware Commits 2020-07-31 11:01:58 UTC
The master branch has been updated by Alan Modra <amodra@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=a896df97b952d4f3feed8068eaa70147d12e0e28

commit a896df97b952d4f3feed8068eaa70147d12e0e28
Author: Alan Modra <amodra@gmail.com>
Date:   Fri Jul 31 16:37:17 2020 +0930

    PR26314, Linking LTO objects with symbols from static and shared libraries
    
    gcc -O2 -g -o ar -Wl,--as-needed arparse.o arlex.o ar.o not-ranlib.o arsup.o rename.o binemul.o emul_vanilla.o bucomm.o version.o filemode.o libbfd-2.35-3.fc33.so libiberty.a -Wl,-R,.
    
    All of the above .o files are lto, leading to libbfd-2.35-3.fc33.so
    not being found needed when loading the IR objects.  That's problem
    number one:  We exclude IR references when deciding a shared library
    is needed.  See PR15146.  Thus none of the libbfd.so symbols are
    loaded before libiberty.a is scanned, and libbfd.so contains copies of
    libiberty.a functions.  We ought to be using the libbfd.so copies
    rather than extracting them from the archive (an object is extracted
    even to satisfy IR symbols).  After lto recompilation, libbfd.so is of
    course found to be needed and loaded.  But that causes more problems.
    The lto recompilation didn't see symbol references from libbfd.so and
    variables like _xexit_cleanup are made local in the recompiled
    objects.  Oops, two copies of them.  Finally, those silly undefined
    symbols in the lto output debug files, combined with definitions in
    both libbfd.so and IR objects result in IR symbols being made
    dynamic.
    
    The main fix here is to revert the PR15146 change to
    elf_link_add_object_symbols.
    
            PR 26314
            * elflink.c (bfd_elf_link_record_dynamic_symbol): Don't allow
            IR symbols to become dynamic.
            (elf_link_add_object_symbols): Don't exclude IR symbols when
            deciding whether an as-needed shared library is needed.
Comment 7 Sourceware Commits 2020-09-04 07:25:04 UTC
The master branch has been updated by Alan Modra <amodra@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=1e3b96fd6cf0c7d018083994ad951ccf92aba582

commit 1e3b96fd6cf0c7d018083994ad951ccf92aba582
Author: Alan Modra <amodra@gmail.com>
Date:   Fri Sep 4 13:54:21 2020 +0930

    Allow plugin syms to mark as-needed shared libs needed
    
    We must tell LTO about symbols in all shared libraries loaded.  That
    means we can't load extra shared libraries after LTO recompilation, at
    least, not those that affect the set of symbols that LTO cares about,
    the IR symbols.
    
    This change will likely result in complaints about --as-needed
    libraries being loaded unnecessarily, but being correct is more
    important than being optimal.  One of the PR15146 tests regresses, and
    while that could be hidden by disabling the missing dso message by
    making it conditional on h->root.non_ir_ref_regular, that would just
    be sweeping a problem under the rug.
    
    bfd/
            PR 15146
            PR 26314
            PR 26530
            * elflink.c (elf_link_add_object_symbols): Do set def_regular
            and ref_regular for IR symbols.  Don't clear dynsym, allowing
            IR symbols to load --as-needed shared libraries, but prevent
            IR symbols from becoming dynamic.
    ld/
            * testsuite/ld-plugin/lto.exp: Don't run pr15146 tests.
            * testsuite/ld-plugin/pr15146.d: Delete.
            * testsuite/ld-plugin/pr15146a.c: Delete.
            * testsuite/ld-plugin/pr15146b.c: Delete.
            * testsuite/ld-plugin/pr15146c.c: Delete.
            * testsuite/ld-plugin/pr15146d.c: Delete.
Comment 8 Sourceware Commits 2020-11-01 23:14:42 UTC
The master branch has been updated by Alan Modra <amodra@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b1a92c635c1ec10fd703302ce1fc4ab3a8515a04

commit b1a92c635c1ec10fd703302ce1fc4ab3a8515a04
Author: Alan Modra <amodra@gmail.com>
Date:   Fri Oct 30 14:56:35 2020 +1030

    PR26806, Suspected linker bug with LTO
    
    This patch reverts most of git commit 1e3b96fd6cf, so IR symbols are
    again not marked def_regular or ref_regular.  That should be enough to
    stop IR symbols from becoming dynamic.  To mark as-needed shared
    libraries referenced by IR symbols, use the referencing BFD rather
    than the ref flags.
    
    bfd/
            PR 15146
            PR 26314
            PR 26530
            PR 26806
            * elflink.c (elf_link_add_object_symbols): Don't set def/ref flags
            for plugin syms.  Do allow plugin syms to mark as-needed libs.
    ld/
            PR 26806
            * testsuite/ld-plugin/lto-19.h,
            * testsuite/ld-plugin/lto-19a.c,
            * testsuite/ld-plugin/lto-19b.c,
            * testsuite/ld-plugin/lto-19c.c: New test.
            * testsuite/ld-plugin/pr26806.c,
            * testsuite/ld-plugin/pr26806.d: New test.
            * testsuite/ld-plugin/lto.exp: Run them.
Comment 9 H.J. Lu 2023-01-23 16:33:31 UTC
Fixed.