If an ELF file specifies a list of RPATHs, for each shared library
dependency search each RPATH and see if the package contains it in that
location.

FS#43239

Signed-off-by: Nicola Squartini <tensor5@gmail.com>
---
 Namcap/rules/sodepends.py | 33 ++++++++++++++++++++++++---------
 1 file changed, 24 insertions(+), 9 deletions(-)

diff --git a/Namcap/rules/sodepends.py b/Namcap/rules/sodepends.py
index 92826af..527f511 100644
--- a/Namcap/rules/sodepends.py
+++ b/Namcap/rules/sodepends.py
@@ -28,6 +28,7 @@ import subprocess
 import tempfile
 import Namcap.package
 from Namcap.ruleclass import *
+from Namcap.rules.rpath import get_rpaths
 
 libcache = {'i686': {}, 'x86-64': {}}
 
@@ -43,7 +44,7 @@ def figurebitsize(line):
     else:
         return 'i686'
 
-def scanlibs(fileobj, filename, sharedlibs):
+def scanlibs(fileobj, filename, sharedlibs, tarlist):
     """
     Run "readelf -d" on a file-like object (e.g. a TarFile)
 
@@ -52,6 +53,7 @@ def scanlibs(fileobj, filename, sharedlibs):
     sharedlibs: a dictionary { library => set(ELF files using that library) }
     """
     shared = re.compile('Shared library: \[(.*)\]')
+    origin = re.compile('^\$ORIGIN|^\$\{ORIGIN\}')
 
     # test magic bytes
     magic = fileobj.read(4)
@@ -71,18 +73,31 @@ def scanlibs(fileobj, filename, sharedlibs):
         var = p.communicate()
         assert(p.returncode == 0)
         for j in var[0].decode('ascii').splitlines():
+            found = False
             n = shared.search(j)
             # Is this a Shared library: line?
             if n != None:
                 # Find out its architecture
                 architecture = figurebitsize(j)
-                try:
-                    libpath = os.path.abspath(
-                            libcache[architecture][n.group(1)])[1:]
-                    sharedlibs.setdefault(libpath, set()).add(filename)
-                except KeyError:
-                    # We didn't know about the library, so add it for fail later
-                    sharedlibs.setdefault(n.group(1), set()).add(filename)
+                # If file has RPATH, check if shared library is already packaged
+                for rpath in get_rpaths(tmp.name):
+                    # Expand $ORIGIN
+                    realrpath = origin.sub(os.path.dirname(filename), rpath)
+                    libpath = os.path.normpath(os.path.join(realrpath, n.group(1)))
+                    # Remove possible leading '/'
+                    libpath = re.sub('^/', '', libpath)
+                    if libpath in tarlist:
+                        sharedlibs.setdefault(libpath, set()).add(filename)
+                        found = True
+                        break
+                if not found:
+                    try:
+                        libpath = os.path.abspath(
+                                libcache[architecture][n.group(1)])[1:]
+                        sharedlibs.setdefault(libpath, set()).add(filename)
+                    except KeyError:
+                        # We didn't know about the library, so add it for fail later
+                        sharedlibs.setdefault(n.group(1), set()).add(filename)
     finally:
         os.unlink(tmp.name)
 
@@ -157,7 +172,7 @@ class SharedLibsRule(TarballRule):
             if not entry.isfile():
                 continue
             f = tar.extractfile(entry)
-            scanlibs(f, entry.name, liblist)
+            scanlibs(f, entry.name, liblist, tar.getnames())
             f.close()
 
         # Ldd all the files and find all the link and script dependencies
--
2.8.2