diff --git a/Makefile.am b/Makefile.am
index 552e2ad0b221367edecaa3ac6e8f551fd693c265..856a745367adaad17f4c38fef6c58722120ede63 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -467,7 +467,7 @@ tests_utils_t_SOURCES                = tests/utils.c        \
                                        tests/test-helpers.h \
                                        utils/library-cmp.c  \
                                        utils/library-cmp.h
-tests_utils_t_LDADD                  = utils/libutils.la $(GLIB_LIBS)
+tests_utils_t_LDADD                  = utils/libutils.la $(GLIB_LIBS) $(LIBELF_LIBS)
 
 test_scripts                         = tests/capture-libs.pl                   \
                                        tests/symbols.pl                        \
diff --git a/utils/library-cmp.c b/utils/library-cmp.c
index d7644525bb1cb6edcdd4d16da3ad2be7b912d7c4..1e7a82626d21b3b6f9a987ae3fa0c7a3f13e78af 100644
--- a/utils/library-cmp.c
+++ b/utils/library-cmp.c
@@ -17,12 +17,699 @@
 
 #include "library-cmp.h"
 
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <libelf.h>
+#include <gelf.h>
 
 #include "debug.h"
 #include "utils.h"
 
+// From binutils/include/elf/common.h (this doesn't appear to be documented
+// anywhere else).
+//
+// /* This flag appears in a Versym structure.  It means that the symbol
+//    is hidden, and is only visible with an explicit version number.
+//    This is a GNU extension.  */
+// #define VERSYM_HIDDEN 0x8000
+//
+// /* This is the mask for the rest of the Versym information.  */
+// #define VERSYM_VERSION 0x7fff
+
+#define VERSYM_HIDDEN 0x8000
+#define VERSYM_VERSION 0x7fff
+
+static int
+bsearch_strcmp_cb( const void *n, const void *ip )
+{
+    const char *needle = n;
+    const char * const *item_p = ip;
+    return strcmp( needle, *item_p );
+}
+
+static int
+qsort_strcmp_cb( const void* s1, const void* s2 )
+{
+    const char * const *a = (const char* const*) s1;
+    const char* const *b = (const char* const*) s2;
+    return strcmp( *a, *b );
+}
+
+/*
+ * string_set_diff_flags:
+ * @STRING_SET_DIFF_ONLY_IN_FIRST: At least one element is in the first set but not the second
+ * @STRING_SET_DIFF_ONLY_IN_SECOND: At least one element is in the second set but not the first
+ * @STRING_SET_DIFF_NONE: All elements are equal
+ *
+ * The result of comparing two sets of strings. If each set
+ * contains elements that the other does not, then
+ * both @STRING_SET_DIFF_ONLY_IN_FIRST
+ * and @STRING_SET_DIFF_ONLY_IN_SECOND will be set.
+ */
+typedef enum
+{
+  STRING_SET_DIFF_ONLY_IN_FIRST = (1 << 0),
+  STRING_SET_DIFF_ONLY_IN_SECOND = (1 << 1),
+  STRING_SET_DIFF_NONE = 0
+} string_set_diff_flags;
+
+/*
+ * compare_string_sets:
+ * @first: the first set to compare
+ * @first_length: number of elements in the first set
+ * @second: the second set to compare
+ * @second_length: number of elements in the second set
+ *
+ * The two sets needs to be ordered because we will use a binary search to do
+ * the comparison.
+ */
+static string_set_diff_flags
+compare_string_sets ( char **first, size_t first_length,
+                      char **second, size_t second_length )
+{
+    string_set_diff_flags result = STRING_SET_DIFF_NONE;
+
+    assert( first != NULL );
+    assert( second != NULL );
+
+    if( first_length > second_length )
+    {
+        result |= STRING_SET_DIFF_ONLY_IN_FIRST;
+    }
+    else
+    {
+        for( size_t i = 0; i < first_length; i++ )
+        {
+            char *found = bsearch( first[i], second, second_length, sizeof(char *), bsearch_strcmp_cb );
+            if( found == NULL )
+            {
+                result |= STRING_SET_DIFF_ONLY_IN_FIRST;
+                break;
+            }
+        }
+    }
+
+    if( first_length < second_length )
+    {
+        result |= STRING_SET_DIFF_ONLY_IN_SECOND;
+    }
+    else
+    {
+        for( size_t i = 0; i < second_length; i++ )
+        {
+            char *found = bsearch( second[i], first, first_length, sizeof(char *), bsearch_strcmp_cb );
+            if( found == NULL )
+            {
+                result |= STRING_SET_DIFF_ONLY_IN_SECOND;
+                break;
+            }
+        }
+    }
+    return result;
+}
+
+static void
+close_elf (Elf **elfp, int *fdp)
+{
+  if (elfp != NULL && *elfp != NULL)
+    {
+      elf_end( *elfp );
+      *elfp = NULL;
+    }
+
+  if (fdp != NULL && *fdp >= 0)
+    {
+      close( *fdp );
+      *fdp = -1;
+    }
+}
+
+/*
+ * open_elf_library:
+ * @path: (type filename): The path where the library is located
+ * @fd: (out) (not optional): Used to return a file descriptor of the
+ *  opened library
+ * @elf: (out) (not optional): Used to return an initialized elf of the
+ *  library
+ * @code: (out) (optional): Used to return an error code on failure
+ * @message: (out) (optional) (nullable): Used to return an error message
+ *  on failure
+ *
+ * Returns: %TRUE if the elf opening succeded, %FALSE otherwise.
+ */
+static bool
+open_elf_library ( const char *path, int *fd, Elf **elf,
+                   int *code, char **message)
+{
+    GElf_Ehdr ehdr;
+    bool result = true;
+
+    assert( *elf == NULL );
+
+    if( elf_version(EV_CURRENT) == EV_NONE )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "elf_version(EV_CURRENT): %s",
+                            elf_errmsg( elf_errno() ) );
+        result = false;
+        goto out;
+    }
+
+    if( ( *fd = open( path, O_RDONLY | O_CLOEXEC, 0 ) ) < 0 )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "failed to open %s", path );
+        result = false;
+        goto out;
+    }
+
+    if( ( *elf = elf_begin( *fd, ELF_C_READ, NULL ) ) == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "elf_begin() failed: %s",
+                            elf_errmsg( elf_errno() ) );
+        result = false;
+        goto out;
+    }
+
+    if( elf_kind( *elf ) != ELF_K_ELF )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "%s is not in ELF format", path );
+        result = false;
+        goto out;
+    }
+
+    if( gelf_getehdr( *elf, &ehdr ) == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "gelf_getehdr() failed: %s",
+                            elf_errmsg( elf_errno() ) );
+        result = false;
+        goto out;
+    }
+    if( ehdr.e_type != ET_DYN )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "%s is not a shared library, elf type is %d",
+                            path, ehdr.e_type );
+        result = false;
+        goto out;
+    }
+
+out:
+    if( !result )
+        close_elf( elf, fd );
+
+    return result;
+}
+
+static void
+print_debug_string_list( char **list, const char *begin_message )
+{
+    if( list == NULL || list[0] == NULL )
+        return;
+
+    if( begin_message != NULL )
+        DEBUG( DEBUG_ELF, "%s", begin_message );
+
+    for( size_t i = 0; list[i] != NULL; i++ )
+        DEBUG( DEBUG_ELF, "%s", list[i] );
+}
+
+/*
+ * get_versions:
+ * @elf: The object's elf of which we want to get the versions
+ * @versions_number: (out) (not optional): The number of versions found
+ * @code: (out) (optional): Used to return an error code on
+ *  failure
+ * @message: (out) (optional) (nullable): Used to return an error message
+ *  on failure
+ *
+ * Returns: (transfer full): The list of versions that the
+ *  shared object has, on failure %NULL.
+ */
+static char **
+get_versions( Elf *elf, size_t *versions_number, int *code, char **message )
+{
+    char **versions = NULL;
+    Elf_Scn *scn = NULL;
+    Elf_Data *data;
+    GElf_Shdr shdr_mem;
+    GElf_Shdr *shdr = NULL;
+    GElf_Verdef def_mem;
+    GElf_Verdef *def;
+    bool found_verdef = false;
+    uintptr_t verdef_ptr = 0;
+    size_t auxoffset;
+    size_t offset = 0;
+    size_t phnum;
+    size_t sh_entsize;
+    ptr_list *versions_list = NULL;
+
+    assert( versions_number != NULL );
+
+    *versions_number = 0;
+
+    if( elf_getphdrnum( elf, &phnum ) < 0 )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to determine the number of program headers: %s",
+                            elf_errmsg( elf_errno() ) );
+        return versions;
+    }
+
+    /* Get the dynamic section */
+    for( size_t i = 0; i < phnum; i++ )
+    {
+        GElf_Phdr phdr_mem;
+        GElf_Phdr *phdr = gelf_getphdr( elf, i, &phdr_mem );
+        if( phdr != NULL && phdr->p_type == PT_DYNAMIC )
+        {
+            scn = gelf_offscn( elf, phdr->p_offset );
+            shdr = gelf_getshdr( scn, &shdr_mem );
+            break;
+        }
+    }
+
+    if( shdr == NULL )
+    {
+        int err = elf_errno();
+        if( err == 0 )
+        {
+            _capsule_set_error( code, message, EINVAL,
+                                "Unable to find the section header from the dynamic section" );
+        }
+        else
+        {
+            _capsule_set_error( code, message, EINVAL,
+                                "Unable to get the section header: %s",
+                                elf_errmsg( err ) );
+        }
+
+        return versions;
+    }
+
+    data = elf_getdata( scn, NULL );
+    if( data == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to get the dynamic section data: %s",
+                            elf_errmsg( elf_errno() ) );
+        return versions;
+    }
+
+    sh_entsize = gelf_fsize( elf, ELF_T_DYN, 1, EV_CURRENT );
+    for( size_t i = 0; i < shdr->sh_size / sh_entsize; i++ )
+    {
+        GElf_Dyn dyn_mem;
+        GElf_Dyn *dyn = gelf_getdyn( data, i, &dyn_mem );
+        if( dyn == NULL )
+            break;
+
+        if( dyn->d_tag == DT_VERDEF )
+        {
+            verdef_ptr = dyn->d_un.d_ptr;
+            found_verdef = true;
+            break;
+        }
+    }
+
+    if( !found_verdef )
+    {
+        DEBUG( DEBUG_ELF, "The version definition table is not available" );
+        versions = calloc( 1, sizeof(char *) );
+        versions[0] = NULL;
+        return versions;
+    }
+    scn = gelf_offscn( elf, verdef_ptr );
+    data = elf_getdata( scn, NULL );
+    if( data == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to get symbols data: %s", elf_errmsg( elf_errno() ) );
+        return versions;
+    }
+
+    def = gelf_getverdef( data, 0, &def_mem );
+    if( def == NULL )
+    {
+        DEBUG( DEBUG_ELF, "Verdef is not available: %s", elf_errmsg( elf_errno() ) );
+        versions = calloc( 1, sizeof(char *) );
+        versions[0] = NULL;
+        return versions;
+    }
+
+    /* Arbitrarily start the list with 8 elements */
+    versions_list = ptr_list_alloc( 8 );
+    while( def != NULL )
+    {
+        GElf_Verdaux aux_mem, *aux;
+        const char *version;
+
+        auxoffset = offset + def->vd_aux;
+        offset += def->vd_next;
+
+        /* The first Verdaux array must exist and it points to the version
+         * definition string that Verdef defines. Every possible additional
+         * Verdaux arrays are the dependencies of said version definition.
+         * In our case we don't need to list the dependencies, so we just
+         * get the first Verdaux of every Verdef. */
+        aux = gelf_getverdaux( data, auxoffset, &aux_mem );
+        if( aux == NULL )
+            continue;
+        version = elf_strptr( elf, shdr->sh_link, aux->vda_name );
+        if( version == NULL )
+            continue;
+
+        if( ( def->vd_flags & VER_FLG_BASE ) == 0 )
+            ptr_list_push_ptr( versions_list, strdup( version ) );
+
+        if( def->vd_next == 0 )
+            def = NULL;
+        else
+            def = gelf_getverdef( data, offset, &def_mem );
+
+    }
+
+    versions = (char **) ptr_list_free_to_array ( versions_list, versions_number );
+    qsort( versions, *versions_number, sizeof(char *), qsort_strcmp_cb );
+    return versions;
+}
+
+static const char * const ignore_symbols[] =
+{
+    /* Libraries on at least SteamOS 2 'brewmaster' sometimes have
+     * symbols that appear to have an empty name. */
+    "",
+
+    /* These symbols can appear in libraries witout actually being part
+     * of anyone's ABI. List taken from dpkg-gensymbols. */
+    "__bss_end__",
+    "__bss_end",
+    "_bss_end__",
+    "__bss_start",
+    "__bss_start__",
+    "__data_start",
+    "__do_global_ctors_aux",
+    "__do_global_dtors_aux",
+    "__do_jv_register_classes",
+    "_DYNAMIC",
+    "_edata",
+    "_end",
+    "__end__",
+    "__exidx_end",
+    "__exidx_start",
+    "_fbss",
+    "_fdata",
+    "_fini",
+    "_ftext",
+    "_GLOBAL_OFFSET_TABLE_",
+    "__gmon_start__",
+    "__gnu_local_gp",
+    "_gp",
+    "_init",
+    "_PROCEDURE_LINKAGE_TABLE_",
+    "_SDA2_BASE_",
+    "_SDA_BASE_",
+};
+
+/*
+ * get_symbols:
+ * @elf: The object's elf of which we want to get the symbols
+ * @symbols_number: (out) (not optional): The number of symbols found
+ * @code: (out) (optional): Used to return an error code on
+ *  failure
+ * @message: (out) (optional) (nullable): Used to return an error message
+ *  on failure
+ *
+ * Returns: (transfer full): The list of symbols that the
+ *  shared object has, on failure %NULL.
+ */
+static char **
+get_symbols ( Elf *elf, size_t *symbols_number, int *code, char **message )
+{
+    char **symbols = NULL;
+    Elf_Scn *scn = NULL;
+    Elf_Scn *scn_sym = NULL;
+    Elf_Scn *scn_ver = NULL;
+    Elf_Scn *scn_verdef = NULL;
+    Elf_Data *data;
+    Elf_Data *sym_data;
+    Elf_Data *versym_data = NULL;
+    Elf_Data *verdef_data = NULL;
+    GElf_Ehdr ehdr;
+    GElf_Shdr shdr_mem;
+    GElf_Shdr *shdr = NULL;
+    bool found_symtab = false;
+    bool found_versym = false;
+    bool found_verdef = false;
+    uintptr_t symtab_ptr = 0;
+    uintptr_t versym_ptr = 0;
+    uintptr_t verdef_ptr = 0;
+    size_t elsize = 0;
+    size_t phnum;
+    size_t sh_entsize;
+    ptr_list *symbols_list = NULL;
+
+    assert( symbols_number != NULL );
+
+    *symbols_number = 0;
+
+    if( elf_getphdrnum( elf, &phnum ) < 0 )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to determine the number of program headers: %s",
+                            elf_errmsg( elf_errno() ) );
+        return symbols;
+    }
+
+    /* Get the dynamic section */
+    for( size_t i = 0; i < phnum; i++ )
+    {
+        GElf_Phdr phdr_mem;
+        GElf_Phdr *phdr = gelf_getphdr( elf, i, &phdr_mem );
+        if( phdr != NULL && phdr->p_type == PT_DYNAMIC )
+        {
+            scn = gelf_offscn( elf, phdr->p_offset );
+            shdr = gelf_getshdr( scn, &shdr_mem );
+            break;
+        }
+    }
+
+    if( shdr == NULL )
+    {
+        int err = elf_errno();
+        if( err == 0 )
+        {
+            _capsule_set_error( code, message, EINVAL,
+                                "Unable to find the section header from the dynamic section" );
+        }
+        else
+        {
+            _capsule_set_error( code, message, EINVAL,
+                                "Unable to get the section header: %s",
+                                elf_errmsg( err ) );
+        }
+
+        return symbols;
+    }
+
+    data = elf_getdata( scn, NULL );
+    if( data == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to get dynamic section data: %s", elf_errmsg( elf_errno() ) );
+        return symbols;
+    }
+
+    sh_entsize = gelf_fsize( elf, ELF_T_DYN, 1, EV_CURRENT );
+
+    for( size_t i = 0; i < shdr->sh_size / sh_entsize; i++ )
+    {
+        GElf_Dyn dyn_mem;
+        GElf_Dyn *dyn = gelf_getdyn (data, i, &dyn_mem);
+        if( dyn == NULL )
+            break;
+
+        switch( dyn->d_tag )
+        {
+            case DT_SYMTAB:
+                symtab_ptr = dyn->d_un.d_ptr;
+                found_symtab = true;
+                break;
+            case DT_VERSYM:
+                versym_ptr = dyn->d_un.d_ptr;
+                found_versym = true;
+                break;
+            case DT_VERDEF:
+                verdef_ptr = dyn->d_un.d_ptr;
+                found_verdef = true;
+                break;
+            default:
+                break;
+        }
+    }
+
+
+    if( !found_symtab )
+    {
+        _capsule_set_error( code, message, EINVAL, "Unable to find the symbols table" );
+        return symbols;
+    }
+    scn_sym = gelf_offscn( elf, symtab_ptr );
+    sym_data = elf_getdata( scn_sym, NULL );
+    if( sym_data == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to get symbols table data: %s", elf_errmsg( elf_errno() ) );
+        return symbols;
+    }
+
+    if( found_versym )
+    {
+        scn_ver = gelf_offscn( elf, versym_ptr );
+        versym_data = elf_getdata( scn_ver, NULL );
+        if( versym_data == NULL )
+        {
+            _capsule_set_error( code, message, EINVAL,
+                                "Unable to get symbols version information data: %s",
+                                elf_errmsg( elf_errno() ) );
+            return symbols;
+        }
+    }
+
+    if( found_verdef )
+    {
+        scn_verdef = gelf_offscn( elf, verdef_ptr );
+        verdef_data = elf_getdata( scn_verdef, NULL );
+        if( verdef_data == NULL )
+        {
+            _capsule_set_error( code, message, EINVAL,
+                                "Unable to get symbols version definition data: %s",
+                                elf_errmsg( elf_errno() ) );
+            return symbols;
+        }
+    }
+
+    if( gelf_getehdr( elf, &ehdr ) == NULL )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Unable to retrieve Ehdr header: %s",
+                            elf_errmsg( elf_errno() ) );
+        return symbols;
+    }
+
+    elsize = gelf_fsize( elf, ELF_T_SYM, 1, ehdr.e_version );
+    if( elsize == 0 )
+    {
+        _capsule_set_error( code, message, EINVAL,
+                            "Size of symbols in Ehdr array is zero: %s",
+                            elf_errmsg( elf_errno() ) );
+        return symbols;
+    }
+
+    /* Arbitrarily start the list with 8 elements */
+    symbols_list = ptr_list_alloc( 8 );
+
+    for( size_t index = 0; index < sym_data->d_size / elsize; index++ )
+    {
+        GElf_Sym *sym;
+        GElf_Sym sym_mem;
+        const char *symbol;
+        GElf_Versym versym_mem;
+        GElf_Versym *versym;
+        GElf_Verdaux aux_mem;
+        GElf_Verdaux *aux = NULL;
+        GElf_Verdef def_mem;
+        GElf_Verdef *def = NULL;
+        bool interesting = true;
+
+        sym = gelf_getsymshndx( sym_data, NULL, index, &sym_mem, NULL );
+
+        /* If the symbol info is not available, or undefined, we skip it */
+        if( sym == NULL || sym->st_shndx == SHN_UNDEF )
+            continue;
+
+        symbol = elf_strptr( elf, shdr->sh_link, sym->st_name );
+        if( symbol == NULL )
+            break;
+
+        for( size_t i = 0; i < N_ELEMENTS( ignore_symbols ); i++ )
+        {
+            if( strcmp( symbol, ignore_symbols[i] ) == 0 )
+            {
+                interesting = false;
+                break;
+            }
+        }
+
+        if ( !interesting )
+        {
+            DEBUG( DEBUG_ELF, "Symbol '%s' is uninteresting", symbol );
+            continue;
+        }
+
+        /* Search the version of the symbol */
+        if( found_versym && found_verdef )
+        {
+            versym = gelf_getversym( versym_data, index, &versym_mem );
+            def = gelf_getverdef( verdef_data, 0, &def_mem );
+
+            size_t auxoffset;
+            size_t offset = 0;
+            while( def != NULL )
+            {
+                auxoffset = offset + def->vd_aux;
+                offset += def->vd_next;
+
+                /* The first Verdaux array must exist and it points to the version
+                * definition string that Verdef defines. Every possible additional
+                * Verdaux arrays are the dependencies of said version definition.
+                * In our case we don't need to list the dependencies, so we just
+                * get the first Verdaux of every Verdef. */
+                aux = gelf_getverdaux( verdef_data, auxoffset, &aux_mem );
+                if( aux == NULL )
+                    continue;
+
+                /* Break if we have found the symbol's version */
+                if( def->vd_ndx == ( *versym & VERSYM_VERSION ) )
+                    break;
+
+                if( def->vd_next == 0 )
+                    def = NULL;
+                else
+                    def = gelf_getverdef( verdef_data, offset, &def_mem );
+            }
+        }
+
+        /* If the symbol is versioned */
+        if( def != NULL && aux != NULL )
+        {
+            const char *version = elf_strptr( elf, shdr->sh_link, aux->vda_name );
+            char *symbol_versioned;
+            xasprintf( &symbol_versioned, "%s@%s", symbol, version );
+            ptr_list_push_ptr( symbols_list, symbol_versioned );
+        }
+        else
+        {
+            ptr_list_push_ptr( symbols_list, strdup( symbol ) );
+        }
+    }
+
+    symbols = (char **) ptr_list_free_to_array ( symbols_list, symbols_number );
+    qsort( symbols, *symbols_number, sizeof(char *), qsort_strcmp_cb );
+    return symbols;
+}
+
 /*
  * library_cmp_by_name:
  * @soname: The library we are interested in, used in debug logging
@@ -102,3 +789,277 @@ library_cmp_by_name( const char *soname,
 
     return ( strverscmp( left_basename, right_basename ) );
 }
+
+/*
+ * library_cmp_by_symbols:
+ * @soname: (type filename): The library we are interested in, used for debug
+ * @container_path: (type filename): The path where the container's @soname is
+ *  located
+ * @provider_path: (type filename): The path where the provider's @soname is
+ *  located
+ *
+ * Attempt to determine whether @soname is older, newer or
+ * the same in the container or the provider inspecting their
+ * symbols.
+ *
+ * Return a strcmp-style result: negative if container < provider,
+ * positive if container > provider, zero if container == provider
+ * or if container and provider are non-comparable.
+ */
+int
+library_cmp_by_symbols( const char *soname,
+                        const char *container_path,
+                        const char *container_root,
+                        const char *provider_path,
+                        const char *provider_root )
+{
+    string_set_diff_flags symbol_result = STRING_SET_DIFF_NONE;
+    int cmp_result = 0;
+    int container_fd = -1;
+    int provider_fd = -1;
+    Elf *container_elf = NULL;
+    Elf *provider_elf = NULL;
+    char **container_symbols = NULL;
+    char **provider_symbols = NULL;
+    size_t container_symbols_number = 0;
+    size_t provider_symbols_number = 0;
+    int code = 0;
+    char *message = NULL;
+    char *symbol_message = NULL;
+
+    if( !open_elf_library( container_path, &container_fd, &container_elf,
+                           &code, &message ) )
+    {
+        DEBUG( DEBUG_TOOL,
+               "an error occurred while opening %s (%d): %s",
+               container_path, code, message );
+        goto out;
+    }
+
+    container_symbols = get_symbols( container_elf, &container_symbols_number,
+                                     &code, &message );
+
+    if( container_symbols == NULL )
+    {
+        warnx( "failed to get container versions for %s (%d): %s",
+               soname, code, message );
+        goto out;
+    }
+
+    xasprintf( &symbol_message, "Container Symbols of %s:", soname );
+    print_debug_string_list( container_symbols, symbol_message );
+    _capsule_clear( &symbol_message );
+
+    if( !open_elf_library( provider_path, &provider_fd, &provider_elf,
+                           &code, &message ) )
+    {
+        DEBUG( DEBUG_TOOL,
+               "an error occurred while opening %s (%d): %s",
+               container_path, code, message );
+        goto out;
+    }
+
+    provider_symbols = get_symbols( provider_elf, &provider_symbols_number,
+                                       &code, &message );
+
+    if( provider_symbols == NULL )
+    {
+        warnx( "failed to get provider versions for %s (%d): %s",
+               soname, code, message );
+        goto out;
+    }
+
+    xasprintf( &symbol_message, "Provider Symbols of %s:", soname );
+    print_debug_string_list( provider_symbols, symbol_message );
+    _capsule_clear( &symbol_message );
+
+    symbol_result = compare_string_sets( container_symbols, container_symbols_number,
+                                         provider_symbols, provider_symbols_number );
+
+    /* In container we have strictly more symbols: don't symlink the one
+     * from the provider */
+    if( symbol_result == STRING_SET_DIFF_ONLY_IN_FIRST )
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the container is newer because its symbols are a strict superset",
+               soname );
+        cmp_result = 1;
+    }
+    /* In provider we have strictly more symbols: create the symlink */
+    else if( symbol_result == STRING_SET_DIFF_ONLY_IN_SECOND )
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the provider is newer because its symbols are a strict superset",
+               soname );
+        cmp_result = -1;
+    }
+    /* With the following two cases we are still unsure which library is newer, so we
+     * will choose the provider */
+    else if( symbol_result == STRING_SET_DIFF_NONE )
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the container and the provider have the same symbols",
+               soname );
+    }
+    else
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the container and the provider have different symbols and neither is a superset of the other",
+               soname );
+    }
+
+out:
+    _capsule_clear( &message );
+    if( container_symbols )
+    {
+        for( size_t i = 0; container_symbols[i] != NULL; i++ )
+            free( container_symbols[i] );
+        free( container_symbols );
+    }
+    if( provider_symbols )
+    {
+        for( size_t i = 0; provider_symbols[i] != NULL; i++ )
+            free( provider_symbols[i] );
+        free( provider_symbols );
+    }
+    close_elf( &container_elf, &container_fd );
+    close_elf( &provider_elf, &provider_fd );
+    return cmp_result;
+
+}
+
+/*
+ * library_cmp_by_versions:
+ * @soname: (type filename): The library we are interested in, used for debug
+ * @container_path: (type filename): The path where the container's @soname is
+ *  located
+ * @provider_path: (type filename): The path where the provider's @soname is
+ *  located
+ *
+ * Attempt to determine whether @soname is older, newer or
+ * the same in the container or the provider inspecting their
+ * symbols versions.
+ *
+ * Return a strcmp-style result: negative if container < provider,
+ * positive if container > provider, zero if container == provider
+ * or if container and provider are non-comparable.
+ */
+int
+library_cmp_by_versions( const char *soname,
+                         const char *container_path,
+                         const char *container_root,
+                         const char *provider_path,
+                         const char *provider_root )
+{
+    string_set_diff_flags version_result = STRING_SET_DIFF_NONE;
+    int cmp_result = 0;
+    int container_fd = -1;
+    int provider_fd = -1;
+    Elf *container_elf = NULL;
+    Elf *provider_elf = NULL;
+    char **container_versions = NULL;
+    char **provider_versions = NULL;
+    size_t container_versions_number = 0;
+    size_t provider_versions_number = 0;
+    int code = 0;
+    char *message = NULL;
+    char *version_message;
+
+    if( !open_elf_library( container_path, &container_fd, &container_elf,
+                           &code, &message ) )
+    {
+        DEBUG( DEBUG_TOOL,
+               "an error occurred while opening %s (%d): %s",
+               container_path, code, message );
+        goto out;
+    }
+
+    container_versions = get_versions( container_elf, &container_versions_number,
+                                       &code, &message );
+
+    if( container_versions == NULL )
+    {
+        warnx( "failed to get container versions for %s (%d): %s",
+               soname, code, message );
+        goto out;
+    }
+
+    xasprintf( &version_message, "Container versions of %s:", soname);
+    print_debug_string_list( container_versions, version_message );
+    _capsule_clear( &version_message );
+
+    if( !open_elf_library( provider_path, &provider_fd, &provider_elf,
+                           &code, &message ) )
+    {
+        DEBUG( DEBUG_TOOL,
+               "an error occurred while opening %s (%d): %s",
+               container_path, code, message );
+        goto out;
+    }
+
+    provider_versions = get_versions( provider_elf, &provider_versions_number,
+                                      &code, &message );
+
+    if( provider_versions == NULL )
+    {
+        warnx( "failed to get provider versions for %s (%d): %s",
+               soname, code, message );
+        goto out;
+    }
+
+    xasprintf( &version_message, "Provider versions of %s:", soname);
+    print_debug_string_list( provider_versions, version_message );
+    _capsule_clear( &version_message );
+
+    version_result = compare_string_sets( container_versions, container_versions_number,
+                                          provider_versions, provider_versions_number );
+
+    /* Version in container is strictly newer: don't symlink the one
+     * from the provider */
+    if( version_result == STRING_SET_DIFF_ONLY_IN_FIRST )
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the container is newer because its version definitions are a strict superset",
+               soname );
+        cmp_result = 1;
+    }
+    /* Version in the provider is strictly newer: create the symlink */
+    else if( version_result == STRING_SET_DIFF_ONLY_IN_SECOND )
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the provider is newer because its version definitions are a strict superset",
+               soname );
+        cmp_result = -1;
+    }
+    /* With the following two cases we are still unsure which library is newer */
+    else if( version_result == STRING_SET_DIFF_NONE )
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the container and the provider have the same symbol versions",
+               soname );
+    }
+    else
+    {
+        DEBUG( DEBUG_TOOL,
+               "%s in the container and the provider have different symbol versions and neither is a superset of the other",
+               soname );
+    }
+
+out:
+    _capsule_clear( &message );
+    if( container_versions )
+    {
+        for( size_t i = 0; container_versions[i] != NULL; i++ )
+            free( container_versions[i] );
+        free( container_versions );
+    }
+    if( provider_versions )
+    {
+        for( size_t i = 0; provider_versions[i] != NULL; i++ )
+            free( provider_versions[i] );
+        free( provider_versions );
+    }
+    close_elf( &container_elf, &container_fd );
+    close_elf( &provider_elf, &provider_fd );
+    return cmp_result;
+}
diff --git a/utils/library-cmp.h b/utils/library-cmp.h
index 4233a85be69a111780bc5537d608e1982c1868bc..4a43f4cf8c513526a8241c336c1899050618c6ed 100644
--- a/utils/library-cmp.h
+++ b/utils/library-cmp.h
@@ -22,3 +22,15 @@ int library_cmp_by_name( const char *soname,
                          const char *container_root,
                          const char *provider_path,
                          const char *provider_root );
+
+int library_cmp_by_symbols( const char *soname,
+                            const char *container_path,
+                            const char *container_root,
+                            const char *provider_path,
+                            const char *provider_root );
+
+int library_cmp_by_versions( const char *soname,
+                             const char *container_path,
+                             const char *container_root,
+                             const char *provider_path,
+                             const char *provider_root );