In order to account for internal library resolution which a package
may implement (useful at least for handling of bundled libraries),
generate implicit runpath entries for any needed sonames which are
provided by the same owner package.

For packages that have bundled versions of system libraries, this will
prevent preserve-libs from unecessarily preserving system libraries as
reported in bug 705736. For the www-client/firefox-bin-72.0.2 package,
this adds an implicit /opt/firefox runpath entry to 15 files.

Bug: https://bugs.gentoo.org/705736
Signed-off-by: Zac Medico <zmed...@gentoo.org>
---
 lib/portage/util/_dyn_libs/LinkageMapELF.py | 63 ++++++++++++++++++---
 1 file changed, 54 insertions(+), 9 deletions(-)

diff --git a/lib/portage/util/_dyn_libs/LinkageMapELF.py 
b/lib/portage/util/_dyn_libs/LinkageMapELF.py
index 92a50b444..70bec116a 100644
--- a/lib/portage/util/_dyn_libs/LinkageMapELF.py
+++ b/lib/portage/util/_dyn_libs/LinkageMapELF.py
@@ -1,7 +1,9 @@
-# Copyright 1998-2019 Gentoo Authors
+# Copyright 1998-2020 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
+import collections
 import errno
+import itertools
 import logging
 import subprocess
 import sys
@@ -14,6 +16,7 @@ from portage import _unicode_encode
 from portage.cache.mappings import slot_dict_class
 from portage.const import EPREFIX
 from portage.dep.soname.multilib_category import compute_multilib_category
+from portage.dep.soname.SonameAtom import SonameAtom
 from portage.exception import CommandNotFound, InvalidData
 from portage.localization import _
 from portage.util import getlibpaths
@@ -328,8 +331,13 @@ class LinkageMapELF(object):
                # Share identical frozenset instances when available,
                # in order to conserve memory.
                frozensets = {}
+               owner_entries = collections.defaultdict(list)
 
-               for owner, location, l in lines:
+               while True:
+                       try:
+                               owner, location, l = lines.pop()
+                       except IndexError:
+                               break
                        l = l.rstrip("\n")
                        if not l:
                                continue
@@ -352,18 +360,55 @@ class LinkageMapELF(object):
                        # exists, map e_machine (entry.arch) to an approximate
                        # multilib category. If all else fails, use e_machine, 
just
                        # as older versions of portage did.
-                       arch = entry.multilib_category
-                       if arch is None:
-                               arch = _approx_multilib_categories.get(
+                       if entry.multilib_category is None:
+                               entry.multilib_category = 
_approx_multilib_categories.get(
                                        entry.arch, entry.arch)
 
-                       obj = entry.filename
-                       soname = entry.soname
+                       entry.filename = normalize_path(entry.filename)
                        expand = {"ORIGIN": os.path.dirname(entry.filename)}
-                       path = frozenset(normalize_path(
+                       entry.runpaths = frozenset(normalize_path(
                                varexpand(x, expand, error_leader=lambda: "%s: 
" % location))
                                for x in entry.runpaths)
-                       path = frozensets.setdefault(path, path)
+                       entry.runpaths = frozensets.setdefault(entry.runpaths, 
entry.runpaths)
+                       owner_entries[owner].append(entry)
+
+               # In order to account for internal library resolution which a 
package
+               # may implement (useful at least for handling of bundled 
libraries),
+               # generate implicit runpath entries for any needed sonames 
which are
+               # provided by the same owner package.
+               for owner, entries in owner_entries.items():
+                       if owner is None:
+                               continue
+
+                       providers = {}
+                       for entry in entries:
+                               if entry.soname:
+                                       
providers[SonameAtom(entry.multilib_category, entry.soname)] = entry
+
+                       for entry in entries:
+                               implicit_runpaths = []
+                               for soname in entry.needed:
+                                       soname_atom = 
SonameAtom(entry.multilib_category, soname)
+                                       provider = providers.get(soname_atom)
+                                       if provider is None:
+                                               continue
+                                       provider_dir = 
os.path.dirname(provider.filename)
+                                       if provider_dir not in entry.runpaths:
+                                               
implicit_runpaths.append(provider_dir)
+
+                               if implicit_runpaths:
+                                       entry.runpaths = frozenset(
+                                               itertools.chain(entry.runpaths, 
implicit_runpaths))
+                                       entry.runpaths = frozensets.setdefault(
+                                               entry.runpaths, entry.runpaths)
+
+               for owner, entry in ((owner, entry)
+                       for (owner, entries) in owner_entries.items()
+                       for entry in entries):
+                       arch = entry.multilib_category
+                       obj = entry.filename
+                       soname = entry.soname
+                       path = entry.runpaths
                        needed = frozenset(entry.needed)
 
                        needed = frozensets.setdefault(needed, needed)
-- 
2.24.1


Reply via email to