From e58bf1fa30b13131d102d504200100e69b6d07df Mon Sep 17 00:00:00 2001
From: Ludovico de Nittis <ludovico.denittis@collabora.com>
Date: Thu, 31 Oct 2019 11:02:08 +0100
Subject: [PATCH] Add support for library's hidden dependencies

Some libraries like "libtheoraenc.so.1" or "libCgGL.so" assumes that you
have already linked to other libraries.

With this commit we add support for those situations with the concept of
"hidden dependencies".

Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com>
---
 debian/control                                |   2 +
 debian/gitlab-ci.yml                          |   1 +
 helpers/inspect-library.c                     |  43 +++++++
 steam-runtime-tools/library-internal.h        |   1 +
 steam-runtime-tools/library.c                 |  47 ++++---
 steam-runtime-tools/system-info.c             | 121 +++++++++++++++++-
 .../i386-linux-gnu/libtheora0.symbols         |   4 +
 tests/expectations/steam-runtime-abi.json     |  48 +++++++
 .../x86_64-linux-gnu/libtheora0.symbols       |   4 +
 .../i386-linux-gnu/libtheora0.symbols         |   4 +
 .../x86_64-linux-gnu/libtheora0.symbols       |   4 +
 .../usr/lib/steamrt/steam-runtime-abi.json    |  48 +++++++
 tests/system-info.c                           |  30 +++++
 13 files changed, 335 insertions(+), 22 deletions(-)
 create mode 100644 tests/expectations/i386-linux-gnu/libtheora0.symbols
 create mode 100644 tests/expectations/steam-runtime-abi.json
 create mode 100644 tests/expectations/x86_64-linux-gnu/libtheora0.symbols
 create mode 100644 tests/fake-steam-runtime/usr/lib/steamrt/expectations/i386-linux-gnu/libtheora0.symbols
 create mode 100644 tests/fake-steam-runtime/usr/lib/steamrt/expectations/x86_64-linux-gnu/libtheora0.symbols
 create mode 100644 tests/fake-steam-runtime/usr/lib/steamrt/steam-runtime-abi.json

diff --git a/debian/control b/debian/control
index 5cffd18fb..1ef16b1ee 100644
--- a/debian/control
+++ b/debian/control
@@ -9,6 +9,7 @@ Build-Depends:
  gtk-doc-tools <!nodoc>,
  libglib2.0-dev,
  libjson-glib-dev (>= 1.0),
+ libtheora0 <!nocheck>,
  libvulkan-dev,
  libxcb1-dev,
  locales <!nocheck> | locales-all <!nocheck>,
@@ -88,6 +89,7 @@ Section: misc
 Depends:
  ${misc:Depends},
  ${shlibs:Depends},
+ libtheora0,
  steam-runtime-tools-bin,
  zlib1g,
 Description:
diff --git a/debian/gitlab-ci.yml b/debian/gitlab-ci.yml
index db0dd4923..cfee92ab4 100644
--- a/debian/gitlab-ci.yml
+++ b/debian/gitlab-ci.yml
@@ -16,6 +16,7 @@ variables:
         libgles2-mesa-dev
         libglib2.0-dev
         libjson-glib-dev
+        libtheora-dev
         libx11-dev
         libxcb1-dev
         libxcomposite-dev
diff --git a/helpers/inspect-library.c b/helpers/inspect-library.c
index 5467eee53..23779009a 100644
--- a/helpers/inspect-library.c
+++ b/helpers/inspect-library.c
@@ -76,10 +76,12 @@ enum
 {
   OPTION_HELP = 1,
   OPTION_DEB_SYMBOLS,
+  OPTION_HIDDEN_DEPENDENCY,
 };
 
 struct option long_options[] =
 {
+    { "hidden-dependency", required_argument, NULL, OPTION_HIDDEN_DEPENDENCY },
     { "deb-symbols", no_argument, NULL, OPTION_DEB_SYMBOLS },
     { "help", no_argument, NULL, OPTION_HELP },
     { NULL, 0, NULL, 0 }
@@ -126,12 +128,17 @@ main (int argc,
   ssize_t chars;
   bool first;
   bool deb_symbols = false;
+  size_t dependency_counter = 0;
   int opt;
 
   while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
     {
       switch (opt)
         {
+          case OPTION_HIDDEN_DEPENDENCY:
+            dependency_counter++;
+            break;
+
           case OPTION_DEB_SYMBOLS:
             deb_symbols = true;
             break;
@@ -152,11 +159,47 @@ main (int argc,
       usage (1);
     }
 
+  const char *hidden_deps[dependency_counter + 1];
+
+  if (dependency_counter > 0)
+    {
+      /* Reset getopt */
+      optind = 1;
+
+      size_t i = 0;
+      while ((opt = getopt_long (argc, argv, "", long_options, NULL)) != -1)
+        {
+          switch (opt)
+            {
+              case OPTION_HIDDEN_DEPENDENCY:
+                hidden_deps[i] = optarg;
+                i++;
+                break;
+
+              default:
+                break;
+            }
+        }
+    }
+
   soname = argv[optind];
   printf ("{\n");
   printf ("  \"");
   print_json_string_content (soname);
   printf ("\": {");
+
+  for (size_t i = 0; i < dependency_counter; i++)
+    {
+      /* We don't call "dlclose" on global hidden dependencies, otherwise ubsan
+       * will report an indirect memory leak */
+      if (dlopen (hidden_deps[i], RTLD_NOW|RTLD_GLOBAL) == NULL)
+        {
+          fprintf (stderr, "Unable to find the dependency library: %s\n", dlerror ());
+          clean_exit (handle);
+          return 1;
+        }
+    }
+
   handle = dlopen (soname, RTLD_NOW);
   if (handle == NULL)
     {
diff --git a/steam-runtime-tools/library-internal.h b/steam-runtime-tools/library-internal.h
index f8f643596..ebc11dea8 100644
--- a/steam-runtime-tools/library-internal.h
+++ b/steam-runtime-tools/library-internal.h
@@ -89,5 +89,6 @@ SrtLibraryIssues _srt_check_library_presence (const char *helpers_path,
                                               const char *soname,
                                               const char *multiarch,
                                               const char *symbols_path,
+                                              const char * const *hidden_deps,
                                               SrtLibrarySymbolsFormat symbols_format,
                                               SrtLibrary **more_details_out);
diff --git a/steam-runtime-tools/library.c b/steam-runtime-tools/library.c
index 3e4a6ad19..21d6d0d7b 100644
--- a/steam-runtime-tools/library.c
+++ b/steam-runtime-tools/library.c
@@ -474,8 +474,8 @@ srt_check_library_presence (const char *soname,
                             SrtLibrary **more_details_out)
 {
   return _srt_check_library_presence (NULL, soname, multiarch,
-                                      symbols_path, symbols_format,
-                                      more_details_out);
+                                      symbols_path, NULL,
+                                      symbols_format, more_details_out);
 }
 
 SrtLibraryIssues
@@ -483,17 +483,14 @@ _srt_check_library_presence (const char *helpers_path,
                              const char *soname,
                              const char *multiarch,
                              const char *symbols_path,
+                             const char * const *hidden_deps,
                              SrtLibrarySymbolsFormat symbols_format,
                              SrtLibrary **more_details_out)
 {
-  gchar *helper = NULL;
+  GPtrArray *argv = NULL;
   gchar *output = NULL;
   gchar *child_stderr = NULL;
   gchar *absolute_path = NULL;
-  const gchar *argv[] =
-    {
-      "inspect-library", "--deb-symbols", soname, symbols_path, NULL
-    };
   int exit_status = -1;
   JsonParser *parser = NULL;
   JsonNode *node = NULL;
@@ -509,6 +506,7 @@ _srt_check_library_presence (const char *helpers_path,
   GStrv my_environ = NULL;
   const gchar *ld_preload;
   gchar *filtered_preload = NULL;
+  argv = g_ptr_array_new_with_free_func (g_free);
 
   g_return_val_if_fail (soname != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR);
   g_return_val_if_fail (multiarch != NULL, SRT_LIBRARY_ISSUES_INTERNAL_ERROR);
@@ -516,31 +514,40 @@ _srt_check_library_presence (const char *helpers_path,
                         SRT_LIBRARY_ISSUES_INTERNAL_ERROR);
   g_return_val_if_fail (_srt_check_not_setuid (), SRT_LIBRARY_ISSUES_INTERNAL_ERROR);
 
+  if (helpers_path == NULL)
+    helpers_path = _srt_get_helpers_path ();
+
+  g_ptr_array_add (argv, g_strdup_printf ("%s/%s-inspect-library", helpers_path, multiarch));
+  g_debug ("Checking library %s integrity with %s", soname, (gchar *)g_ptr_array_index (argv, 0));
+
   switch (symbols_format)
     {
       case SRT_LIBRARY_SYMBOLS_FORMAT_PLAIN:
-        argv[1] = soname;
-        argv[2] = symbols_path;
-        argv[3] = NULL;
+        g_ptr_array_add (argv, g_strdup (soname));
+        g_ptr_array_add (argv, g_strdup (symbols_path));
         break;
 
       case SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS:
-        /* do nothing, argv is already set up for this */
+        g_ptr_array_add (argv, g_strdup ("--deb-symbols"));
+        g_ptr_array_add (argv, g_strdup (soname));
+        g_ptr_array_add (argv, g_strdup (symbols_path));
         break;
 
       default:
         g_return_val_if_reached (SRT_LIBRARY_ISSUES_CANNOT_LOAD);
     }
 
-  if (symbols_path == NULL)
-    issues |= SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS;
+  for (gsize i = 0; hidden_deps != NULL && hidden_deps[i] != NULL; i++)
+    {
+      g_ptr_array_add (argv, g_strdup ("--hidden-dependency"));
+      g_ptr_array_add (argv, g_strdup (hidden_deps[i]));
+    }
 
-  if (helpers_path == NULL)
-    helpers_path = _srt_get_helpers_path ();
+  /* NULL terminate the array */
+  g_ptr_array_add (argv, NULL);
 
-  helper = g_strdup_printf ("%s/%s-inspect-library", helpers_path, multiarch);
-  argv[0] = helper;
-  g_debug ("Checking library %s integrity with %s", soname, helper);
+  if (symbols_path == NULL)
+    issues |= SRT_LIBRARY_ISSUES_UNKNOWN_EXPECTATIONS;
 
   my_environ = g_get_environ ();
   ld_preload = g_environ_getenv (my_environ, "LD_PRELOAD");
@@ -551,7 +558,7 @@ _srt_check_library_presence (const char *helpers_path,
     }
 
   if (!g_spawn_sync (NULL,       /* working directory */
-                     (gchar **) argv,
+                     (gchar **) argv->pdata,
                      my_environ, /* envp */
                      0,          /* flags */
                      NULL,       /* child setup */
@@ -658,9 +665,9 @@ out:
   g_strfreev (missing_symbols);
   g_strfreev (misversioned_symbols);
   g_strfreev (dependencies);
+  g_ptr_array_free (argv, TRUE);
   g_free (absolute_path);
   g_free (child_stderr);
-  g_free (helper);
   g_free (output);
   g_free (filtered_preload);
   g_clear_error (&error);
diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c
index 7619c3de6..96726f4b7 100644
--- a/steam-runtime-tools/system-info.c
+++ b/steam-runtime-tools/system-info.c
@@ -45,6 +45,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <json-glib/json-glib.h>
 
 /**
  * SECTION:system-info
@@ -92,6 +93,7 @@ struct _SrtSystemInfo
   /* Multiarch tuple to use for helper executables in cases where it
    * shouldn't matter, or %NULL to use the compiled-in default */
   GQuark primary_multiarch_tuple;
+  GHashTable *cached_hidden_deps;
   struct
   {
     /* GQuark => MaybeLocale */
@@ -409,6 +411,8 @@ srt_system_info_finalize (GObject *object)
   g_free (self->expectations);
   g_free (self->helpers_path);
   g_strfreev (self->env);
+  if (self->cached_hidden_deps)
+    g_hash_table_unref (self->cached_hidden_deps);
 
   G_OBJECT_CLASS (srt_system_info_parent_class)->finalize (object);
 }
@@ -607,6 +611,111 @@ ensure_expectations (SrtSystemInfo *self)
   return self->expectations[0] != '\0';
 }
 
+static void
+ensure_hidden_deps (SrtSystemInfo *self)
+{
+  if (self->cached_hidden_deps == NULL)
+    {
+      JsonParser *parser = NULL;
+      JsonNode *node = NULL;
+      JsonArray *libraries_array = NULL;
+      JsonArray *hidden_libraries_array = NULL;
+      JsonObject *object;
+      gchar *soname = NULL;
+      gchar *path = NULL;
+      GPtrArray *arr = NULL;
+      GError *error = NULL;
+
+      self->cached_hidden_deps = g_hash_table_new_full (g_str_hash,
+                                                        g_str_equal,
+                                                        g_free,
+                                                        (GDestroyNotify) g_strfreev);
+
+      if (!ensure_expectations (self))
+        {
+          g_debug ("Hidden dependencies parsing skipped because of unknown expectations");
+          goto out;
+        }
+
+      path = g_build_filename (self->expectations, "steam-runtime-abi.json", NULL);
+
+      /* Currently, in a standard Steam installation, we have the abi JSON one level up
+      * from the expectations folder */
+      if (!g_file_test (path, G_FILE_TEST_EXISTS))
+        {
+          g_free (path);
+          path = g_build_filename (self->expectations, "..", "steam-runtime-abi.json", NULL);
+        }
+
+      parser = json_parser_new ();
+      if (!json_parser_load_from_file (parser, path, &error))
+        {
+          g_debug ("Error parsing the expected JSON object in \"%s\": %s", path, error->message);
+          goto out;
+        }
+
+      node = json_parser_get_root (parser);
+      object = json_node_get_object (node);
+
+      if (!json_object_has_member (object, "shared_libraries"))
+        {
+          g_debug ("No \"shared_libraries\" in the JSON object \"%s\"", path);
+          goto out;
+        }
+
+      libraries_array = json_object_get_array_member (object, "shared_libraries");
+      /* If there are no libraries in the parsed JSON file we simply return */
+      if (libraries_array == NULL || json_array_get_length (libraries_array) == 0)
+        goto out;
+
+      for (guint i = 0; i < json_array_get_length (libraries_array); i++)
+        {
+          node = json_array_get_element (libraries_array, i);
+          if (!JSON_NODE_HOLDS_OBJECT (node))
+            continue;
+
+          object = json_node_get_object (node);
+
+          GList *members = json_object_get_members (object);
+          if (members == NULL)
+            continue;
+
+          soname = g_strdup (members->data);
+          g_list_free (members);
+
+          object = json_object_get_object_member (object, soname);
+          if (!json_object_has_member (object, "hidden_dependencies"))
+            {
+              g_free (soname);
+              continue;
+            }
+
+          hidden_libraries_array = json_object_get_array_member (object, "hidden_dependencies");
+          if (hidden_libraries_array == NULL || json_array_get_length (hidden_libraries_array) == 0)
+            {
+              g_free (soname);
+              continue;
+            }
+
+          arr = g_ptr_array_new_full (json_array_get_length (hidden_libraries_array) + 1, g_free);
+
+          for (guint j = 0; j < json_array_get_length (hidden_libraries_array); j++)
+            g_ptr_array_add (arr, g_strdup (json_array_get_string_element (hidden_libraries_array, j)));
+
+          g_ptr_array_add (arr, NULL);
+
+          g_debug ("%s soname hidden dependencies have been parsed", soname);
+          g_hash_table_insert (self->cached_hidden_deps, g_steal_pointer (&soname), g_ptr_array_free (arr, FALSE));
+        }
+
+      out:
+        g_free (path);
+        g_clear_error (&error);
+        if (parser)
+          g_clear_object (&parser);
+    }
+}
+
 static void
 ensure_overrides_cached (SrtSystemInfo *self)
 {
@@ -974,6 +1083,8 @@ srt_system_info_check_libraries (SrtSystemInfo *self,
       goto out;
     }
 
+  ensure_hidden_deps (self);
+
   while ((filename = g_dir_read_name (dir)))
     {
       char *line = NULL;
@@ -1007,10 +1118,12 @@ srt_system_info_check_libraries (SrtSystemInfo *self,
                 * found it, as an argument. */
               SrtLibrary *library = NULL;
               char *soname = g_strdup (strsep (&pointer_into_line, " \t"));
+              gchar **hidden_deps = g_hash_table_lookup (self->cached_hidden_deps, soname);
               abi->cached_combined_issues |= _srt_check_library_presence (self->helpers_path,
                                                                           soname,
                                                                           multiarch_tuple,
                                                                           symbols_file,
+                                                                          (const gchar * const *)hidden_deps,
                                                                           SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS,
                                                                           &library);
               g_hash_table_insert (abi->cached_results, soname, library);
@@ -1042,7 +1155,6 @@ srt_system_info_check_libraries (SrtSystemInfo *self,
       g_dir_close (dir);
 
     return ret;
-
 }
 
 /**
@@ -1110,6 +1222,8 @@ srt_system_info_check_library (SrtSystemInfo *self,
         }
     }
 
+  ensure_hidden_deps (self);
+
   while (dir != NULL && (filename = g_dir_read_name (dir)))
     {
       if (!g_str_has_suffix (filename, ".symbols"))
@@ -1141,10 +1255,12 @@ srt_system_info_check_library (SrtSystemInfo *self,
               char *soname_found = g_strdup (strsep (&pointer_into_line, " \t"));
               if (g_strcmp0 (soname_found, soname) == 0)
                 {
+                  gchar **hidden_deps = g_hash_table_lookup (self->cached_hidden_deps, soname);
                   issues = _srt_check_library_presence (self->helpers_path,
                                                         soname_found,
                                                         multiarch_tuple,
                                                         symbols_file,
+                                                        (const gchar * const *)hidden_deps,
                                                         SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS,
                                                         &library);
                   g_hash_table_insert (abi->cached_results, soname_found, library);
@@ -1169,6 +1285,7 @@ srt_system_info_check_library (SrtSystemInfo *self,
                                         soname,
                                         multiarch_tuple,
                                         NULL,
+                                        NULL,
                                         SRT_LIBRARY_SYMBOLS_FORMAT_DEB_SYMBOLS,
                                         &library);
   g_hash_table_insert (abi->cached_results, g_strdup (soname), library);
@@ -1299,7 +1416,7 @@ srt_system_info_check_graphics (SrtSystemInfo *self,
  *
  * Returns: (transfer full) (type SrtGraphics): A list of #SrtGraphics objects
  * representing the items tested and results.
- * Free with 'glist_free_full(list, g_object_unref)`.
+ * Free with 'g_list_free_full(list, g_object_unref)`.
  */
 GList * srt_system_info_check_all_graphics (SrtSystemInfo *self,
     const char *multiarch_tuple)
diff --git a/tests/expectations/i386-linux-gnu/libtheora0.symbols b/tests/expectations/i386-linux-gnu/libtheora0.symbols
new file mode 100644
index 000000000..a46156c32
--- /dev/null
+++ b/tests/expectations/i386-linux-gnu/libtheora0.symbols
@@ -0,0 +1,4 @@
+# Cut-down version of libtheora0:i386.symbols, to illustrate what we expect
+# to find here
+libtheoraenc.so.1 libtheora0 #MINVER#
+# No symbols listed here yet
diff --git a/tests/expectations/steam-runtime-abi.json b/tests/expectations/steam-runtime-abi.json
new file mode 100644
index 000000000..a85385ce7
--- /dev/null
+++ b/tests/expectations/steam-runtime-abi.json
@@ -0,0 +1,48 @@
+{
+ "architectures": {
+  "i386-linux-gnu": {
+   "dpkg_name": "i386"
+  },
+  "x86_64-linux-gnu": {
+   "dpkg_name": "amd64"
+  }
+ },
+ "extra_debs": {
+  "libs": [
+   "dconf-gsettings-backend", 
+   "gtk2-engines"
+  ]
+ },
+ "private_libraries": [
+  {
+   "libVkLayer_*.so": {
+    "deb": "libvulkan1"
+   }
+  }
+ ],
+ "shared_libraries": [
+  {
+   "libglut.so.3": {
+    "deb": "freeglut3"
+   }
+  },
+  "libacl.so.1",
+  {
+   "libtheoraenc.so.1": {
+    "deb": "libtheora0",
+    "hidden_dependencies": [
+     "libtheoradec.so.1"
+    ]
+   }
+  },
+  {
+   "libtWithHiddens.so.1": {
+    "deb": "libtWithHiddens0",
+    "hidden_dependencies": [
+     "firstHidden.so.0",
+     "secondHidden.so.3"
+    ]
+   }
+  }
+ ]
+}
diff --git a/tests/expectations/x86_64-linux-gnu/libtheora0.symbols b/tests/expectations/x86_64-linux-gnu/libtheora0.symbols
new file mode 100644
index 000000000..0d054e654
--- /dev/null
+++ b/tests/expectations/x86_64-linux-gnu/libtheora0.symbols
@@ -0,0 +1,4 @@
+# Cut-down version of libtheora0:amd64.symbols, to illustrate what we expect
+# to find here
+libtheoraenc.so.1 libtheora0 #MINVER#
+# No symbols listed here yet
diff --git a/tests/fake-steam-runtime/usr/lib/steamrt/expectations/i386-linux-gnu/libtheora0.symbols b/tests/fake-steam-runtime/usr/lib/steamrt/expectations/i386-linux-gnu/libtheora0.symbols
new file mode 100644
index 000000000..a46156c32
--- /dev/null
+++ b/tests/fake-steam-runtime/usr/lib/steamrt/expectations/i386-linux-gnu/libtheora0.symbols
@@ -0,0 +1,4 @@
+# Cut-down version of libtheora0:i386.symbols, to illustrate what we expect
+# to find here
+libtheoraenc.so.1 libtheora0 #MINVER#
+# No symbols listed here yet
diff --git a/tests/fake-steam-runtime/usr/lib/steamrt/expectations/x86_64-linux-gnu/libtheora0.symbols b/tests/fake-steam-runtime/usr/lib/steamrt/expectations/x86_64-linux-gnu/libtheora0.symbols
new file mode 100644
index 000000000..0d054e654
--- /dev/null
+++ b/tests/fake-steam-runtime/usr/lib/steamrt/expectations/x86_64-linux-gnu/libtheora0.symbols
@@ -0,0 +1,4 @@
+# Cut-down version of libtheora0:amd64.symbols, to illustrate what we expect
+# to find here
+libtheoraenc.so.1 libtheora0 #MINVER#
+# No symbols listed here yet
diff --git a/tests/fake-steam-runtime/usr/lib/steamrt/steam-runtime-abi.json b/tests/fake-steam-runtime/usr/lib/steamrt/steam-runtime-abi.json
new file mode 100644
index 000000000..a85385ce7
--- /dev/null
+++ b/tests/fake-steam-runtime/usr/lib/steamrt/steam-runtime-abi.json
@@ -0,0 +1,48 @@
+{
+ "architectures": {
+  "i386-linux-gnu": {
+   "dpkg_name": "i386"
+  },
+  "x86_64-linux-gnu": {
+   "dpkg_name": "amd64"
+  }
+ },
+ "extra_debs": {
+  "libs": [
+   "dconf-gsettings-backend", 
+   "gtk2-engines"
+  ]
+ },
+ "private_libraries": [
+  {
+   "libVkLayer_*.so": {
+    "deb": "libvulkan1"
+   }
+  }
+ ],
+ "shared_libraries": [
+  {
+   "libglut.so.3": {
+    "deb": "freeglut3"
+   }
+  },
+  "libacl.so.1",
+  {
+   "libtheoraenc.so.1": {
+    "deb": "libtheora0",
+    "hidden_dependencies": [
+     "libtheoradec.so.1"
+    ]
+   }
+  },
+  {
+   "libtWithHiddens.so.1": {
+    "deb": "libtWithHiddens0",
+    "hidden_dependencies": [
+     "firstHidden.so.0",
+     "secondHidden.so.3"
+    ]
+   }
+  }
+ ]
+}
diff --git a/tests/system-info.c b/tests/system-info.c
index bff4ea2ac..cb330f515 100644
--- a/tests/system-info.c
+++ b/tests/system-info.c
@@ -225,6 +225,36 @@ check_libraries_result (GList *libraries)
 
   g_assert_true (seen_libc);
 
+  /* Test third library */
+  l = g_list_next (l);
+  library = l->data;
+  g_assert_nonnull (library);
+  g_assert_cmpstr (srt_library_get_soname (library), ==, "libtheoraenc.so.1");
+  missing_symbols = srt_library_get_missing_symbols (library);
+  g_assert_nonnull (missing_symbols);
+  g_assert_cmpstr (missing_symbols[0], ==, NULL);
+
+  g_assert_cmpint (srt_library_get_issues (library), ==, SRT_LIBRARY_ISSUES_NONE);
+
+  misversioned_symbols = srt_library_get_misversioned_symbols (library);
+  g_assert_nonnull (misversioned_symbols);
+  g_assert_cmpstr (misversioned_symbols[0], ==, NULL);
+
+  dependencies = srt_library_get_dependencies (library);
+  g_assert_nonnull (dependencies);
+  g_assert_cmpstr (dependencies[0], !=, NULL);
+  seen_libc = FALSE;
+
+  for (gsize i = 0; dependencies[i] != NULL; i++)
+    {
+      g_debug ("libtheoraenc.so.1 depends on %s", dependencies[i]);
+
+      if (strstr (dependencies[i], "/libc.so.") != NULL)
+        seen_libc = TRUE;
+    }
+
+  g_assert_true (seen_libc);
+
   /* Test last library */
   l = g_list_next (l);
   library = l->data;
-- 
GitLab