Skip to content
Snippets Groups Projects

sodepends: fix listing binaries, check libdepends, libprovides

Merged Balló György requested to merge bgyorgy/namcap:fix-sodepends-list into master
1 unresolved thread
+ 65
13
@@ -35,41 +35,43 @@ from elftools.elf.dynamic import DynamicSection
libcache = {'i686': {}, 'x86-64': {}}
def scanlibs(fileobj, filename, custom_libs):
def scanlibs(fileobj, filename, custom_libs, liblist, libdepends, libprovides):
"""
Find shared libraries in a file-like binary object
If it depends on a library, store that library's path.
returns: a dictionary { library => set(ELF files using that library) }
If it depends on a library or provides one, store that library's path.
"""
if not is_elf(fileobj):
return {}
elffile = ELFFile(fileobj)
sharedlibs = defaultdict(set)
for section in elffile.iter_sections():
if not isinstance(section, DynamicSection):
continue
for tag in section.iter_tags():
# DT_SONAME means it provides a library; ignore unversioned (mostly internal) libraries
if tag.entry.d_tag == 'DT_SONAME' and not tag.soname.endswith('.so'):
soname = tag.soname.rsplit('.so', 1)[0] + '.so'
libprovides[soname].add(filename)
# DT_NEEDED means shared library
if tag.entry.d_tag != 'DT_NEEDED':
continue
bitsize = elffile.elfclass
architecture = {32:'i686', 64:'x86-64'}[bitsize]
libname = tag.needed
soname = libname.rsplit('.so', 1)[0] + '.so'
libdepends[soname].add(filename)
if libname in custom_libs:
sharedlibs[custom_libs[libname][1:]].add(filename)
liblist[custom_libs[libname][1:]].add(filename)
continue
try:
libpath = os.path.abspath(
libcache[architecture][libname])[1:]
sharedlibs[libpath].add(filename)
liblist[libpath].add(filename)
except KeyError:
# We didn't know about the library, so add it for fail later
sharedlibs[libname].add(filename)
return sharedlibs
liblist[libname].add(filename)
def finddepends(liblist):
"""
@@ -77,9 +79,13 @@ def finddepends(liblist):
Returns:
dependlist -- a dictionary { package => set(libraries) }
libdependlist -- a dictionary { soname => package }
orphans -- the list of libraries without owners
missing_provides -- the list of sonames without providers
"""
dependlist = defaultdict(set)
libdependlist = {}
missing_provides = {}
somatches = {}
actualpath = {}
@@ -108,9 +114,16 @@ def finddepends(liblist):
if j == actualpath[k] or (j.startswith(actualpath[k]) and so_end.match(j[len(actualpath[k]):])):
dependlist[pkg.name].add(k)
foundlibs.add(k)
# Check if the dependency can be satisfied by soname
soname = os.path.basename(k).rsplit('.so', 1)[0] + '.so'
stripped_provides = [Namcap.package.strip_depend_info(d) for d in pkg.provides]
if soname in stripped_provides:
libdependlist[soname] = pkg.name
else:
missing_provides[soname] = pkg.name
orphans = list(knownlibs - foundlibs)
return dependlist, orphans
return dependlist, libdependlist, orphans, missing_provides
def filllibcache():
var = subprocess.Popen('ldconfig -p',
@@ -133,8 +146,12 @@ class SharedLibsRule(TarballRule):
name = "sodepends"
description = "Checks dependencies caused by linked shared libraries"
def analyze(self, pkginfo, tar):
liblist = {}
liblist = defaultdict(set)
libdepends = defaultdict(set)
libprovides = defaultdict(set)
dependlist = {}
libdependlist = {}
missing_provides = {}
filllibcache()
os.environ['LC_ALL'] = 'C'
pkg_so_files = ['/' + n for n in tar.getnames() if '.so' in n]
@@ -151,16 +168,20 @@ class SharedLibsRule(TarballRule):
for n in pkg_so_files:
if any(n.startswith(rp) for rp in rpaths):
rpath_files[os.path.basename(n)] = n
liblist.update(scanlibs(f, entry.name, rpath_files))
scanlibs(f, entry.name, rpath_files, liblist, libdepends, libprovides)
f.close()
# Ldd all the files and find all the link and script dependencies
dependlist, orphans = finddepends(liblist)
dependlist, libdependlist, orphans, missing_provides = finddepends(liblist)
# Handle "no package associated" errors
self.warnings.extend([("library-no-package-associated %s", i)
for i in orphans])
# Hanle when a required soname does not provided by the associated package yet
self.infos.extend([("libdepends-missing-provides %s %s", (i, missing_provides[i]))
for i in missing_provides])
# Print link-level deps
for pkg, libraries in dependlist.items():
if isinstance(libraries, set):
@@ -173,6 +194,37 @@ class SharedLibsRule(TarballRule):
))
self.infos.append(("link-level-dependence %s in %s", (pkg, str(files))))
# Check for soname dependencies, filter out internal dependencies
libdependlist = dict(filter(lambda elem: elem[1] != pkginfo["name"], libdependlist.items()))
for i in libdependlist:
if i in pkginfo["depends"]:
self.infos.append(("libdepends-detected-satisfied %s %s (%s)", (i, libdependlist[i], str(list(libdepends[i])))))
    • This code is looking a little like LISP, and since the same deeply tested construction is use 5 times here it seems like a local function might have been in order, but my Python foo isn't up to suggesting something robust and idiomatic. If nothing comes to mind based on this suggestion feel free to dismiss...

Please register or sign in to reply
continue
if i in pkginfo["optdepends"]:
self.warnings.append(("libdepends-detected-but-optional %s %s (%s)", (i, libdependlist[i], str(list(libdepends[i])))))
continue
self.warnings.append(("libdepends-detected-not-included %s %s (%s)", (i, libdependlist[i], str(list(libdepends[i])))))
for i in pkginfo["depends"]:
if i.endswith('.so') and i not in libdependlist:
self.warnings.append(("libdepends-not-needed %s", i))
self.infos.append(("libdepends-by-namcap-sight depends=(%s)", ' '.join(libdependlist) ))
# Check provided libraries
for i in libprovides:
if i in pkginfo["provides"]:
self.infos.append(("libprovides-satisfied %s %s", (i, str(list(libprovides[i])))))
continue
self.warnings.append(("libprovides-unsatisfied %s %s", (i, str(list(libprovides[i])))))
for i in pkginfo["provides"]:
if i.endswith('.so') and i not in libprovides:
self.errors.append(("libprovides-missing %s", i))
self.infos.append(("libprovides-by-namcap-sight provides=(%s)", ' '.join(libprovides) ))
# Check for packages in testing
for i in dependlist.keys():
p = Namcap.package.load_testing_package(i)
Loading