From d11a6dec7e1a0cdfdddb2b537433ef348e625d94 Mon Sep 17 00:00:00 2001
From: Ludovico de Nittis <ludovico.denittis@collabora.com>
Date: Thu, 12 Mar 2020 15:03:30 +0100
Subject: [PATCH] Add a special Mesa DRIs search path to cover Ubuntu 16.04

Ubuntu 16.04 is older than GLVND and it places Mesa loaders in
${libdir}/mesa and the DRIs in ${libdir}/dri.

So if we find a loader in a path that ends with "/mesa" we try to look
one directory above.

Signed-off-by: Ludovico de Nittis <ludovico.denittis@collabora.com>
---
 steam-runtime-tools/graphics.c                | 37 +++++++++++-
 tests/graphics.c                              | 54 ++++++++++++++++++
 tests/meson.build                             |  1 +
 tests/mock-ubuntu-64-bit-inspect-library.c    | 56 +++++++++++++++++++
 .../ubuntu16/usr/lib/dri/radeonsi_dri.so      |  0
 .../lib/mock-ubuntu-64-bit/dri/i965_dri.so    |  0
 .../lib/mock-ubuntu-64-bit/dri/radeon_dri.so  |  0
 .../dri/radeonsi_drv_video.so                 |  0
 .../usr/lib/mock-ubuntu-64-bit/libva.so.1     |  0
 .../lib/mock-ubuntu-64-bit/mesa/libGL.so.1    |  0
 .../vdpau/libvdpau_r600.so.1                  |  1 +
 .../vdpau/libvdpau_r600.so.1.0.0              |  0
 .../vdpau/libvdpau_radeonsi.so                |  1 +
 .../vdpau/libvdpau_radeonsi.so.1              |  1 +
 .../vdpau/libvdpau_radeonsi.so.1.0.0          |  0
 15 files changed, 150 insertions(+), 1 deletion(-)
 create mode 100644 tests/mock-ubuntu-64-bit-inspect-library.c
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/dri/radeonsi_dri.so
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/i965_dri.so
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/radeon_dri.so
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/radeonsi_drv_video.so
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/libva.so.1
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/mesa/libGL.so.1
 create mode 120000 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1.0.0
 create mode 120000 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so
 create mode 120000 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1
 create mode 100644 tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1.0.0

diff --git a/steam-runtime-tools/graphics.c b/steam-runtime-tools/graphics.c
index c51a9e434..f06d36833 100644
--- a/steam-runtime-tools/graphics.c
+++ b/steam-runtime-tools/graphics.c
@@ -2600,6 +2600,21 @@ _srt_get_extra_modules_directory (const gchar *library_search_path,
   g_return_val_if_fail (multiarch_tuple != NULL, NULL);
 
   dir = g_strdup (library_search_path);
+
+  /* If the loader path ends with "/mesa" we try to look one directory above.
+   * For example this is how Ubuntu 16.04 works, the loaders are in ${libdir}/mesa
+   * and the DRI modules in ${libdir}/dri */
+  if (g_str_has_suffix (dir, "/mesa"))
+    {
+      dir[strlen (dir) - strlen ("/mesa") + 1] = '\0';
+      /* Remove the trailing slash */
+      if (g_strcmp0 (dir, "/") != 0)
+        dir[strlen (dir) - 1] = '\0';
+    }
+
+  ret = g_list_prepend (ret, g_build_filename (dir, "dri", NULL));
+  g_debug ("Looking in lib directory: %s", (const char *) ret->data);
+
   lib_multiarch = g_strdup_printf ("/lib/%s", multiarch_tuple);
 
   if (!g_str_has_suffix (dir, lib_multiarch))
@@ -2632,6 +2647,8 @@ _srt_get_extra_modules_directory (const gchar *library_search_path,
   ret = g_list_prepend (ret, g_build_filename (dir, libqual, "dri", NULL));
   g_debug ("Looking in libQUAL directory: %s", (const char *) ret->data);
 
+  ret = g_list_sort (ret, (GCompareFunc) strcmp);
+
 out:
   g_free (lib_multiarch);
   g_free (dir);
@@ -2810,9 +2827,11 @@ _srt_get_modules_full (const char *sysroot,
   static const char *const vdpau_loaders[] = { "libvdpau.so.1", NULL };
   const gchar *env_override;
   const gchar *drivers_path;
+  const gchar *force_elf_class = NULL;
   gchar *flatpak_info;
   GHashTable *drivers_set;
   gboolean is_extra = FALSE;
+  int driver_class;
 
   g_return_if_fail (multiarch_tuple != NULL);
   g_return_if_fail (drivers_out != NULL);
@@ -2846,6 +2865,11 @@ _srt_get_modules_full (const char *sysroot,
   else
     drivers_path = g_getenv (env_override);
 
+  if (envp != NULL)
+    force_elf_class = g_environ_getenv (envp, "SRT_TEST_FORCE_ELF");
+  else
+    force_elf_class = g_getenv ("SRT_TEST_FORCE_ELF");
+
   if (sysroot == NULL)
     sysroot = "/";
 
@@ -3017,7 +3041,18 @@ _srt_get_modules_full (const char *sysroot,
                                       libdir_driver, is_extra, module, drivers_out);
         }
 
-      int driver_class = _srt_get_library_class (driver_canonical_path);
+      if (force_elf_class)
+        {
+          if (g_strcmp0 (force_elf_class, "64") == 0)
+            driver_class = ELFCLASS64;
+          else
+            driver_class = ELFCLASS32;
+        }
+      else
+        {
+          driver_class = _srt_get_library_class (driver_canonical_path);
+        }
+
       const GList *this_extra_path;
       if (driver_class != ELFCLASSNONE)
         {
diff --git a/tests/graphics.c b/tests/graphics.c
index 3aa913f58..a693b8586 100644
--- a/tests/graphics.c
+++ b/tests/graphics.c
@@ -28,6 +28,7 @@
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
+#include <libelf.h>
 
 #include <glib.h>
 #include <glib/gstdio.h>
@@ -2027,6 +2028,57 @@ test_dri_fedora (Fixture *f,
   g_strfreev (envp);
 }
 
+static void
+test_dri_ubuntu16 (Fixture *f,
+                   gconstpointer context)
+{
+  SrtSystemInfo *info;
+  gchar **envp;
+  gchar *sysroot;
+  GList *dri;
+  GList *va_api;
+  const gchar *multiarch_tuples[] = {"mock-ubuntu-64-bit", NULL};
+  const gchar *dri_suffixes[] = {NULL};
+  const gchar *dri_suffixes_extra[] = {"/lib/dri/radeonsi_dri.so",
+                                       "/lib/mock-ubuntu-64-bit/dri/i965_dri.so",
+                                       "/lib/mock-ubuntu-64-bit/dri/radeon_dri.so",
+                                       NULL};
+  const gchar *va_api_suffixes[] = {"/lib/mock-ubuntu-64-bit/dri/radeonsi_drv_video.so",
+                                    NULL};
+
+  sysroot = g_build_filename (f->srcdir, "sysroots", "ubuntu16", NULL);
+  envp = g_get_environ ();
+  envp = g_environ_setenv (envp, "SRT_TEST_SYSROOT", sysroot, TRUE);
+  envp = g_environ_setenv (envp, "SRT_TEST_FORCE_ELF", "64", TRUE);
+  envp = g_environ_unsetenv (envp, "LIBGL_DRIVERS_PATH");
+  envp = g_environ_unsetenv (envp, "LIBVA_DRIVERS_PATH");
+
+  info = srt_system_info_new (NULL);
+  srt_system_info_set_environ (info, envp);
+  srt_system_info_set_sysroot (info, sysroot);
+  srt_system_info_set_helpers_path (info, f->builddir);
+
+  dri = srt_system_info_list_dri_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_NONE);
+  check_list_suffixes (dri, dri_suffixes, SRT_GRAPHICS_DRI_MODULE);
+  g_list_free_full (dri, g_object_unref);
+
+  dri = srt_system_info_list_dri_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_INCLUDE_ALL);
+  check_list_suffixes (dri, dri_suffixes_extra, SRT_GRAPHICS_DRI_MODULE);
+  g_list_free_full (dri, g_object_unref);
+
+  va_api = srt_system_info_list_va_api_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_NONE);
+  check_list_suffixes (va_api, va_api_suffixes, SRT_GRAPHICS_VAAPI_MODULE);
+  g_list_free_full (va_api, g_object_unref);
+
+  va_api = srt_system_info_list_va_api_drivers (info, multiarch_tuples[0], SRT_DRIVER_FLAGS_INCLUDE_ALL);
+  check_list_suffixes (va_api, va_api_suffixes, SRT_GRAPHICS_VAAPI_MODULE);
+  g_list_free_full (va_api, g_object_unref);
+
+  g_object_unref (info);
+  g_free (sysroot);
+  g_strfreev (envp);
+}
+
 static void
 test_dri_with_env (Fixture *f,
                    gconstpointer context)
@@ -2578,6 +2630,8 @@ main (int argc,
               setup, test_dri_debian10, teardown);
   g_test_add ("/graphics/dri/fedora", Fixture, NULL,
               setup, test_dri_fedora, teardown);
+  g_test_add ("/graphics/dri/ubuntu16", Fixture, NULL,
+              setup, test_dri_ubuntu16, teardown);
   g_test_add ("/graphics/dri/with_env", Fixture, NULL,
               setup, test_dri_with_env, teardown);
   g_test_add ("/graphics/dri/flatpak", Fixture, NULL,
diff --git a/tests/meson.build b/tests/meson.build
index 9fded0f17..9f03b5b25 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -230,6 +230,7 @@ foreach helper : [
   'mock-good-wflinfo',
   'mock-mixed-check-gl',
   'mock-software-wflinfo',
+  'mock-ubuntu-64-bit-inspect-library',
 ]
   executable(
     helper,
diff --git a/tests/mock-ubuntu-64-bit-inspect-library.c b/tests/mock-ubuntu-64-bit-inspect-library.c
new file mode 100644
index 000000000..4e7db2cee
--- /dev/null
+++ b/tests/mock-ubuntu-64-bit-inspect-library.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2019 Collabora Ltd.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+
+int
+main (int argc,
+      char **argv)
+{
+  g_return_val_if_fail (argc == 2, EXIT_FAILURE);
+
+  gchar **envp = g_get_environ ();
+  gchar *path = NULL;
+
+  /* If we need to locate "libGL.so.1" we return a canonical Ubuntu 16.04 style 64 bit folder, under
+   * the "mesa" subfolder */
+  if (g_strcmp0 (argv[1], "libGL.so.1") == 0)
+    path = g_build_filename (g_environ_getenv (envp, "SRT_TEST_SYSROOT"), "usr", "lib",
+                             "mock-ubuntu-64-bit", "mesa", argv[1], NULL);
+  else
+    path = g_build_filename (g_environ_getenv (envp, "SRT_TEST_SYSROOT"), "usr", "lib",
+                             "mock-ubuntu-64-bit", argv[1], NULL);
+
+  /* Return a JSON like if we found the given soname */
+  printf ("{\n\t\"%s\": {\n"
+          "\t\t\"path\": \"%s\"\n"
+          "\t}\n"
+          "}\n", argv[1], path);
+  g_free (path);
+  g_strfreev (envp);
+  return 0;
+}
diff --git a/tests/sysroots/ubuntu16/usr/lib/dri/radeonsi_dri.so b/tests/sysroots/ubuntu16/usr/lib/dri/radeonsi_dri.so
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/i965_dri.so b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/i965_dri.so
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/radeon_dri.so b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/radeon_dri.so
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/radeonsi_drv_video.so b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/dri/radeonsi_drv_video.so
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/libva.so.1 b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/libva.so.1
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/mesa/libGL.so.1 b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/mesa/libGL.so.1
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1 b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1
new file mode 120000
index 000000000..664517965
--- /dev/null
+++ b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1
@@ -0,0 +1 @@
+libvdpau_r600.so.1.0.0
\ No newline at end of file
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1.0.0 b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_r600.so.1.0.0
new file mode 100644
index 000000000..e69de29bb
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so
new file mode 120000
index 000000000..7d720b573
--- /dev/null
+++ b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so
@@ -0,0 +1 @@
+libvdpau_radeonsi.so.1.0.0
\ No newline at end of file
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1 b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1
new file mode 120000
index 000000000..7d720b573
--- /dev/null
+++ b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1
@@ -0,0 +1 @@
+libvdpau_radeonsi.so.1.0.0
\ No newline at end of file
diff --git a/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1.0.0 b/tests/sysroots/ubuntu16/usr/lib/mock-ubuntu-64-bit/vdpau/libvdpau_radeonsi.so.1.0.0
new file mode 100644
index 000000000..e69de29bb
-- 
GitLab