diff --git a/.gitignore b/.gitignore
index f177cb4b903b312165439cb30f46c9c3b325d2fd..5a9de428f2129b954e3a775c66a961f3b5887cac 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
 /_build/
 /build/
 /builddir/
+/helpers/_generated_*/
 /libcapsule/
 /libcapsule_*.dsc
 /libcapsule_*.tar.[gx]z
diff --git a/bin/system-info.c b/bin/system-info.c
index 88e76992859dbcd031c87453d1a26f621d71ec6e..c9403bd635b65ed1188fc444e53adab069040b3c 100644
--- a/bin/system-info.c
+++ b/bin/system-info.c
@@ -1046,6 +1046,10 @@ main (int argc,
       GList *va_api_list = NULL;
       GList *vdpau_list = NULL;
       GList *glx_list = NULL;
+      g_autofree gchar *libdl_lib = NULL;
+      g_autofree gchar *libdl_platform = NULL;
+      g_autoptr(GError) libdl_lib_error = NULL;
+      g_autoptr(GError) libdl_platform_error = NULL;
       const char *ld_so;
 
       json_builder_set_member_name (builder, multiarch_tuples[i]);
@@ -1054,6 +1058,35 @@ main (int argc,
       can_run = srt_system_info_can_run (info, multiarch_tuples[i]);
       json_builder_add_boolean_value (builder, can_run);
 
+      json_builder_set_member_name (builder, "libdl-LIB");
+      libdl_lib = srt_system_info_dup_libdl_lib (info, multiarch_tuples[i],
+                                                 &libdl_lib_error);
+      if (libdl_lib != NULL)
+        {
+          json_builder_add_string_value (builder, libdl_lib);
+        }
+      else
+        {
+          json_builder_begin_object (builder);
+          _srt_json_builder_add_error_members (builder, libdl_lib_error);
+          json_builder_end_object (builder);
+        }
+
+      json_builder_set_member_name (builder, "libdl-PLATFORM");
+      libdl_platform = srt_system_info_dup_libdl_platform (info,
+                                                           multiarch_tuples[i],
+                                                           &libdl_platform_error);
+      if (libdl_platform != NULL)
+        {
+          json_builder_add_string_value (builder, libdl_platform);
+        }
+      else
+        {
+          json_builder_begin_object (builder);
+          _srt_json_builder_add_error_members (builder, libdl_platform_error);
+          json_builder_end_object (builder);
+        }
+
       ld_so = srt_architecture_get_expected_runtime_linker (multiarch_tuples[i]);
 
       if (ld_so != NULL)
diff --git a/bin/system-info.md b/bin/system-info.md
index 9b9e705173bb62fcd66a1837e311e80d22431a90..233885754584f865e9cadf30b238bfec7121b4b4 100644
--- a/bin/system-info.md
+++ b/bin/system-info.md
@@ -354,6 +354,27 @@ keys:
     :   A boolean value indicating whether a simple executable for this
         architecture was run successfully.
 
+    **libdl-LIB**
+    :   A string containing the dynamic linker expansion of the literal token
+        `$LIB`/`${LIB}`. See `ld.so(8)` section "Dynamic string tokens" for
+        more details. However if an error occurred while trying to determine
+        the expansion value, **libdl-LIB** will instead contain an object with
+        the following details of the error:
+
+        **error-domain**
+        :   A machine-readable string indicating a category of errors.
+
+        **error-code**
+        :   A small integer indicating a specific error. Its meaning depends
+            on the **error-domain**.
+
+        **error**
+        :   A human-readable error message.
+
+    **libdl-PLATFORM**
+    :   The same as the **libdl-LIB** described above, but describing the
+        literal token `$PLATFORM`/`${PLATFORM}`.
+
     **runtime-linker**
     :   If the multiarch tuple is a known one, an object with the
         following keys and values describing the runtime linker **ld.so**(8):
diff --git a/helpers/create-meson-in-subdir.py b/helpers/create-meson-in-subdir.py
new file mode 100644
index 0000000000000000000000000000000000000000..2e029e4faf52683081b31cebf0ec850b710dd265
--- /dev/null
+++ b/helpers/create-meson-in-subdir.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+#
+# Copyright © 2021 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.
+
+import sys
+from pathlib import Path
+
+dir_path = Path(sys.argv[1])
+dir_path.mkdir(exist_ok=True)
+
+meson_path = dir_path / "meson.build"
+
+with meson_path.open('w') as text:
+    text.write(sys.argv[2])
diff --git a/helpers/detect.c b/helpers/detect.c
new file mode 100644
index 0000000000000000000000000000000000000000..1734537ff1ed16ce7f4a76be08514067c72ad047
--- /dev/null
+++ b/helpers/detect.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2021 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 <dlfcn.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int
+main (int argc,
+      char **argv)
+{
+  void *handle = NULL;
+  const char *err;
+
+#if defined(_SRT_LIB_PATH) && defined(_SRT_FUNCTION)
+  handle = dlopen (_SRT_LIB_PATH, RTLD_NOW);
+  if (handle == NULL)
+    {
+      fprintf (stderr, "Unable to find the library: %s\n", dlerror ());
+      return 1;
+    }
+
+  char *(*fn)(void) = dlsym (handle, _SRT_FUNCTION);
+  err = dlerror ();
+  if (err != NULL)
+    {
+      fprintf (stderr, "Unable to load the library function: %s\n",
+                       dlerror ());
+      dlclose (handle);
+      return 1;
+    }
+
+  printf ("%s\n", fn());
+
+  dlclose (handle);
+  return 0;
+#else
+  return 1;
+#endif
+}
diff --git a/helpers/identify-lib.c b/helpers/identify-lib.c
new file mode 100644
index 0000000000000000000000000000000000000000..80cf3eefc97849ae452e3a6e7029ff4703380bad
--- /dev/null
+++ b/helpers/identify-lib.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2021 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 "identify-lib.h"
+
+
+const
+char *_srt_identify_lib (void)
+{
+  #if defined(_SRT_LIB_VALUE)
+    return _SRT_LIB_VALUE;
+  #else
+    return NULL;
+  #endif
+}
diff --git a/helpers/identify-lib.h b/helpers/identify-lib.h
new file mode 100644
index 0000000000000000000000000000000000000000..66a10a71fc27be776de1b270444e73284794548b
--- /dev/null
+++ b/helpers/identify-lib.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2021 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.
+ */
+
+__attribute__((visibility("default")))
+const char *_srt_identify_lib (void);
diff --git a/helpers/identify-platform.c b/helpers/identify-platform.c
new file mode 100644
index 0000000000000000000000000000000000000000..3364c59b8a91700723fe1253fdca73bdc966b947
--- /dev/null
+++ b/helpers/identify-platform.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2021 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 "identify-platform.h"
+
+
+const
+char *_srt_identify_platform (void)
+{
+  #if defined(_SRT_PLATFORM_VALUE)
+    return _SRT_PLATFORM_VALUE;
+  #else
+    return NULL;
+  #endif
+}
diff --git a/helpers/identify-platform.h b/helpers/identify-platform.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d78777bdaf8d726e49c290f933466cde4fdea75
--- /dev/null
+++ b/helpers/identify-platform.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright © 2021 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.
+ */
+
+__attribute__((visibility("default")))
+const char *_srt_identify_platform (void);
diff --git a/helpers/meson.build b/helpers/meson.build
index 0034412f4816194bba6042eec2128f545687f889..7da618da416bc3720ba32ee1146544dd9b3d4409 100644
--- a/helpers/meson.build
+++ b/helpers/meson.build
@@ -1,4 +1,4 @@
-# Copyright © 2019 Collabora Ltd.
+# Copyright © 2019-2021 Collabora Ltd.
 #
 # SPDX-License-Identifier: MIT
 #
@@ -136,4 +136,112 @@ executable(
   install_rpath : pkglibexec_rpath,
 )
 
+executable(
+  multiarch + '-detect-lib',
+  'detect.c',
+  c_args : [
+    '-D_SRT_LIB_PATH="${ORIGIN}/' + multiarch + '/${LIB}/libidentify-lib.so"',
+    '-D_SRT_FUNCTION="_srt_identify_lib"',
+  ],
+  dependencies : [libdl],
+  include_directories : project_include_dirs,
+  install : true,
+  install_dir : pkglibexecdir,
+)
+
+executable(
+  multiarch + '-detect-platform',
+  'detect.c',
+  c_args : [
+    '-D_SRT_LIB_PATH="${ORIGIN}/' + multiarch + '/${PLATFORM}/libidentify-platform.so"',
+    '-D_SRT_FUNCTION="_srt_identify_platform"',
+  ],
+  dependencies : [libdl],
+  include_directories : project_include_dirs,
+  install : true,
+  install_dir : pkglibexecdir,
+)
+
+# Due to some Meson's limitations it is not possible to build multiple times
+# the same library, with the same name, and place it in a different directory.
+# For this reason we generate the required meson.build files in subdirectories
+# to avoid conflicts.
+# https://github.com/mesonbuild/meson/issues/2320
+# https://github.com/mesonbuild/meson/issues/4019
+
+meson_library_template = '''
+shared_module(
+  'identify-lib',
+  '@0@',
+  c_args : ['-D_SRT_LIB_VALUE="@1@"',],
+  install : true,
+  install_dir : join_paths(
+    pkglibexecdir,
+    multiarch,
+    '@1@',
+  )
+)
+'''
+
+# This was supposed to be a dictionary but meson < 0.53.0 doesn't support
+# the usage of variables containing a string value as keys.
+components = [['lib', '../identify-lib.c']]
+
+if multiarch != ''
+  components += [
+    ['lib' / multiarch, '../../identify-lib.c'],
+    # This is to support Ubuntu 12.04 where $LIB wrongly expanded to just
+    # the multiarch
+    [multiarch, '../identify-lib.c'],
+  ]
+endif
+
+if multiarch == 'i386-linux-gnu'
+  components += [['lib32', '../identify-lib.c']]
+  platform_dirs = ['i386', 'i486', 'i586', 'i686']
+elif multiarch == 'x86_64-linux-gnu'
+  components += [['lib64', '../identify-lib.c']]
+  platform_dirs = ['x86_64', 'haswell', 'xeon_phi']
+endif
+
+foreach component : components
+  subdir = '_generated_' + component[0]
+  meson_library = meson_library_template.format(component[1], component[0])
+  run_command(
+    python,
+    'create-meson-in-subdir.py',
+    meson.current_source_dir() / subdir,
+    meson_library,
+    check : true,
+  )
+  subdir(subdir)
+endforeach
+
+meson_platform_template = '''
+shared_module(
+  'identify-platform',
+  '../identify-platform.c',
+  c_args : ['-D_SRT_PLATFORM_VALUE="@0@"',],
+  install : true,
+  install_dir : join_paths(
+    pkglibexecdir,
+    multiarch,
+    '@0@',
+  )
+)
+'''
+
+foreach platform_dir : platform_dirs
+  generated_dir = '_generated_' + platform_dir
+  meson_platform = meson_platform_template.format(platform_dir)
+  run_command(
+    python,
+    'create-meson-in-subdir.py',
+    meson.current_source_dir() / generated_dir,
+    meson_platform,
+    check : true,
+  )
+  subdir(generated_dir)
+endforeach
+
 # vim:set sw=2 sts=2 et:
diff --git a/steam-runtime-tools/libdl-internal.h b/steam-runtime-tools/libdl-internal.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc3b70179a36918ee4521ebb22d480d507501119
--- /dev/null
+++ b/steam-runtime-tools/libdl-internal.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright © 2021 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 <glib.h>
+#include <glib-object.h>
+
+G_GNUC_INTERNAL gchar *_srt_libdl_detect_platform (gchar **envp,
+                                                   const char *helpers_path,
+                                                   const char *multiarch_tuple,
+                                                   GError **error);
+
+G_GNUC_INTERNAL gchar *_srt_libdl_detect_lib (gchar **envp,
+                                              const char *helpers_path,
+                                              const char *multiarch_tuple,
+                                              GError **error);
diff --git a/steam-runtime-tools/libdl.c b/steam-runtime-tools/libdl.c
new file mode 100644
index 0000000000000000000000000000000000000000..7223f4366ca559578025d635459abbb8f1b756ab
--- /dev/null
+++ b/steam-runtime-tools/libdl.c
@@ -0,0 +1,128 @@
+/*
+ * Copyright © 2021 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 "steam-runtime-tools/libdl-internal.h"
+
+#include "steam-runtime-tools/glib-backports-internal.h"
+#include "steam-runtime-tools/utils.h"
+#include "steam-runtime-tools/utils-internal.h"
+
+static gchar *
+_srt_libdl_run_helper (gchar **envp,
+                       const char *helpers_path,
+                       const char *multiarch_tuple,
+                       const char *helper_name,
+                       GError **error)
+{
+  g_autoptr(GPtrArray) argv = NULL;
+  g_auto(GStrv) my_environ = NULL;
+  g_autofree gchar *child_stdout = NULL;
+  g_autofree gchar *child_stderr = NULL;
+  gint wait_status;
+  int exit_status;
+  gsize len;
+
+  g_return_val_if_fail (envp != NULL, NULL);
+  g_return_val_if_fail (helper_name != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail (_srt_check_not_setuid (), NULL);
+
+  if (multiarch_tuple == NULL)
+    multiarch_tuple = _SRT_MULTIARCH;
+
+  argv = _srt_get_helper (helpers_path, multiarch_tuple, helper_name,
+                          SRT_HELPER_FLAGS_NONE, error);
+
+  if (argv == NULL)
+    return NULL;
+
+  my_environ = _srt_filter_gameoverlayrenderer_from_envp (envp);
+
+  g_ptr_array_add (argv, NULL);
+
+  g_debug ("Running %s", (const char *) g_ptr_array_index (argv, 0));
+
+  if (!g_spawn_sync (NULL,    /* working directory */
+                     (gchar **) argv->pdata,
+                     my_environ,
+                     G_SPAWN_DEFAULT,
+                     _srt_child_setup_unblock_signals,
+                     NULL,    /* user data */
+                     &child_stdout,
+                     &child_stderr,
+                     &wait_status,
+                     error))
+    return NULL;
+
+  if (!WIFEXITED (wait_status))
+    {
+      g_debug ("-> wait status: %d", wait_status);
+      return glnx_null_throw (error, "Unhandled wait status %d (killed by signal?)",
+                              wait_status);
+    }
+
+  exit_status = WEXITSTATUS (wait_status);
+  g_debug ("-> exit status: %d", exit_status);
+  g_debug ("-> stderr: %s", child_stderr);
+
+  if (exit_status != 0)
+    return glnx_null_throw (error, "%s", child_stderr);
+
+  len = strlen (child_stdout);
+
+  /* Emulate shell $() */
+  if (len > 0 && child_stdout[len - 1] == '\n')
+    child_stdout[len - 1] = '\0';
+
+  g_debug ("-> %s", child_stdout);
+
+  return g_steal_pointer(&child_stdout);
+}
+
+gchar *
+_srt_libdl_detect_platform (gchar **envp,
+                            const char *helpers_path,
+                            const char *multiarch_tuple,
+                            GError **error)
+{
+  return _srt_libdl_run_helper (envp,
+                                helpers_path,
+                                multiarch_tuple,
+                                "detect-platform",
+                                error);
+}
+
+gchar *
+_srt_libdl_detect_lib (gchar **envp,
+                       const char *helpers_path,
+                       const char *multiarch_tuple,
+                       GError **error)
+{
+  return _srt_libdl_run_helper (envp,
+                                helpers_path,
+                                multiarch_tuple,
+                                "detect-lib",
+                                error);
+}
diff --git a/steam-runtime-tools/meson.build b/steam-runtime-tools/meson.build
index 7f345e2915407017dedac78a76ab8b82519cd0f0..1f5ea949bd7d893c3723cd4640f5e5c03263d8ae 100644
--- a/steam-runtime-tools/meson.build
+++ b/steam-runtime-tools/meson.build
@@ -40,6 +40,8 @@ libsteamrt_sources = [
     'json-glib-backports-internal.h',
     'json-utils.c',
     'json-utils-internal.h',
+    'libdl-internal.h',
+    'libdl.c',
     'library-internal.h',
     'library.c',
     'locale-internal.h',
diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c
index 4819412376824da22add142a6e5d4ebb33da59b1..4c38f40284a1565447c8b4cf491c39278e3807e4 100644
--- a/steam-runtime-tools/system-info.c
+++ b/steam-runtime-tools/system-info.c
@@ -37,6 +37,7 @@
 #include "steam-runtime-tools/graphics.h"
 #include "steam-runtime-tools/graphics-internal.h"
 #include "steam-runtime-tools/json-utils-internal.h"
+#include "steam-runtime-tools/libdl-internal.h"
 #include "steam-runtime-tools/library-internal.h"
 #include "steam-runtime-tools/locale-internal.h"
 #include "steam-runtime-tools/os-internal.h"
@@ -261,6 +262,11 @@ typedef struct
   SrtLibraryIssues cached_combined_issues;
   gboolean libraries_cache_available;
 
+  gchar *libdl_lib;
+  GError *libdl_lib_error;
+  gchar *libdl_platform;
+  GError *libdl_platform_error;
+
   GHashTable *cached_graphics_results;
   SrtGraphicsIssues cached_combined_graphics_issues;
   gboolean graphics_cache_available;
@@ -339,6 +345,10 @@ abi_free (gpointer self)
 
   g_free (abi->runtime_linker_resolved);
   g_clear_error (&abi->runtime_linker_error);
+  g_free (abi->libdl_lib);
+  g_clear_error (&abi->libdl_lib_error);
+  g_free (abi->libdl_platform);
+  g_clear_error (&abi->libdl_platform_error);
   g_slice_free (Abi, self);
 }
 
@@ -698,6 +708,72 @@ get_runtime_linker_from_report (SrtSystemInfo *self,
     }
 }
 
+static void
+get_libdl_from_report (SrtSystemInfo *self,
+                       Abi *abi,
+                       JsonObject *json_arch_obj)
+{
+  gsize i;
+
+  /* Struct to avoid code duplication between all the similar libdl entries */
+  typedef struct
+  {
+    const gchar *member_name;
+    gchar **abi_member;
+    GError **abi_member_error;
+  } LibdlStruct;
+
+  LibdlStruct libdl_elements[] =
+  {
+    { "libdl-LIB", &abi->libdl_lib, &abi->libdl_lib_error },
+    { "libdl-PLATFORM", &abi->libdl_platform, &abi->libdl_platform_error },
+  };
+
+  g_return_if_fail (abi->libdl_lib == NULL);
+  g_return_if_fail (abi->libdl_lib_error == NULL);
+  g_return_if_fail (abi->libdl_platform == NULL);
+  g_return_if_fail (abi->libdl_platform_error == NULL);
+
+  for (i = 0; i < G_N_ELEMENTS (libdl_elements); i++)
+    {
+      JsonNode *subnode;
+      const LibdlStruct *libdl_element = &libdl_elements[i];
+
+      if (!json_object_has_member (json_arch_obj, libdl_element->member_name))
+        continue;
+
+      subnode = json_object_get_member (json_arch_obj, libdl_element->member_name);
+
+      if (JSON_NODE_HOLDS_VALUE (subnode))
+        {
+          *libdl_element->abi_member = g_strdup (json_node_get_string (subnode));
+        }
+      else
+        {
+          JsonObject *json_element_obj;
+          GQuark error_domain;
+          int error_code;
+          const char *error_message;
+
+          json_element_obj = json_object_get_object_member (json_arch_obj,
+                                                            libdl_element->member_name);
+
+          error_domain = g_quark_from_string (json_object_get_string_member_with_default (json_element_obj,
+                                                                                          "error-domain",
+                                                                                          NULL));
+          error_code = json_object_get_int_member_with_default (json_element_obj,
+                                                                "error-code",
+                                                                -1);
+          error_message = json_object_get_string_member_with_default (json_element_obj,
+                                                                      "error",
+                                                                      "Unknown error");
+
+          g_set_error_literal (libdl_element->abi_member_error, error_domain, error_code,
+                               error_message);
+        }
+    }
+}
+
 /**
  * srt_system_info_new_from_json:
  * @path: (not nullable) (type filename): Path to a JSON report
@@ -806,6 +882,8 @@ srt_system_info_new_from_json (const char *path,
 
           abi->can_run = _srt_architecture_can_run_from_report (json_arch_obj);
 
+          get_libdl_from_report (info, abi, json_arch_obj);
+
           abi->libraries_cache_available = TRUE;
           abi->cached_combined_issues = _srt_library_get_issues_from_report (json_arch_obj);
 
@@ -4411,3 +4489,147 @@ srt_system_info_check_runtime_linker (SrtSystemInfo *self,
       g_return_val_if_reached (FALSE);
     }
 }
+
+/**
+ * srt_system_info_dup_libdl_lib:
+ * @self: The #SrtSystemInfo object
+ * @multiarch_tuple: A multiarch tuple defining an ABI, as printed
+ *  by `gcc -print-multiarch` in the Steam Runtime
+ * @error: Used to raise an error on failure.
+ *
+ * Return the expansion of the dynamic linker string token `$LIB`,
+ * if possible.
+ *
+ * If the library path to be loaded with `dlopen()` or similar contains
+ * the literal tokens `$LIB` or `${LIB}`, the dynamic linker will replace
+ * them with the library directory returned by this function. See `ld.so(8)`
+ * section "Dynamic string tokens" for more details.
+ *
+ * Because there is currently no glibc API to determine how this token would
+ * be expanded, only a finite number of known values can currently be detected.
+ * If called on a platform where `$LIB` has a different expansion, this
+ * function will return %NULL.
+ *
+ * %NULL is also returned if an error occurs while attempting to determine the
+ * expansion of this token.
+ *
+ * Typical values look like `lib`, `lib32`, `lib64`, `lib/x86-64-linux-gnu`
+ * or `lib/i386-linux-gnu`.
+ *
+ * Returns: (transfer full) (type filename) (nullable): A filename, or %NULL.
+ */
+gchar *
+srt_system_info_dup_libdl_lib (SrtSystemInfo *self,
+                               const char *multiarch_tuple,
+                               GError **error)
+{
+  Abi *abi = NULL;
+
+  g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL);
+  g_return_val_if_fail (multiarch_tuple != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  abi = ensure_abi_unless_immutable (self, multiarch_tuple);
+
+  if (abi == NULL)
+    return glnx_null_throw (error, "ABI \"%s\" not included in report",
+                            multiarch_tuple);
+
+  /* If we cached already the result, we return it */
+  if (abi->libdl_lib != NULL)
+    {
+      return g_strdup (abi->libdl_lib);
+    }
+  else if (abi->libdl_lib_error != NULL)
+    {
+      if (error != NULL)
+        *error = g_error_copy (abi->libdl_lib_error);
+      return NULL;
+    }
+
+  if (self->immutable_values)
+    return glnx_null_throw (error, "libdl LIB for ABI \"%s\" not included in report",
+                            multiarch_tuple);
+
+  abi->libdl_lib = _srt_libdl_detect_lib (self->env,
+                                          self->helpers_path,
+                                          multiarch_tuple,
+                                          &abi->libdl_lib_error);
+
+  if (abi->libdl_lib_error != NULL && error != NULL)
+    *error = g_error_copy (abi->libdl_lib_error);
+
+  return g_strdup (abi->libdl_lib);
+}
+
+/**
+ * srt_system_info_dup_libdl_platform:
+ * @self: The #SrtSystemInfo object
+ * @multiarch_tuple: A multiarch tuple defining an ABI, as printed
+ *  by `gcc -print-multiarch` in the Steam Runtime
+ * @error: Used to raise an error on failure.
+ *
+ * Return the expansion of the dynamic linker string token `$PLATFORM`,
+ * if possible.
+ *
+ * If the library path to be loaded with `dlopen()` or similar contains
+ * the literal tokens `$PLATFORM` or `${PLATFORM}`, the dynamic linker will
+ * replace them with the library directory returned by this function. See
+ * `ld.so(8)` section "Dynamic string tokens" for more details.
+ *
+ * Because there is currently no glibc API to determine how this token would
+ * be expanded, only a finite number of known values can currently be detected.
+ * If called on a platform where `$PLATFORM` has a different expansion, this
+ * function will return %NULL.
+ *
+ * %NULL is also returned if an error occurs while attempting to determine the
+ * expansion of this token.
+ *
+ * Typical values look like `x86_64`, `haswell`, `xeon_phi`, `i386`, `i486`,
+ * `i586`, or `i686`.
+ *
+ * Returns: (transfer full) (type filename) (nullable): A filename, or %NULL.
+ */
+gchar *
+srt_system_info_dup_libdl_platform (SrtSystemInfo *self,
+                                    const char *multiarch_tuple,
+                                    GError **error)
+{
+  Abi *abi = NULL;
+
+  g_return_val_if_fail (SRT_IS_SYSTEM_INFO (self), NULL);
+  g_return_val_if_fail (multiarch_tuple != NULL, NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  abi = ensure_abi_unless_immutable (self, multiarch_tuple);
+
+  if (abi == NULL)
+    return glnx_null_throw (error, "ABI \"%s\" not included in report",
+                            multiarch_tuple);
+
+  /* If we cached already the result, we return it */
+  if (abi->libdl_platform != NULL)
+    {
+      return g_strdup (abi->libdl_platform);
+    }
+  else if (abi->libdl_platform_error != NULL)
+    {
+      if (error != NULL)
+        *error = g_error_copy (abi->libdl_platform_error);
+      return NULL;
+    }
+
+  if (self->immutable_values)
+    return glnx_null_throw (error, "libdl PLATFORM for ABI \"%s\" not included in report",
+                            multiarch_tuple);
+
+  abi->libdl_platform = _srt_libdl_detect_platform (self->env,
+                                                    self->helpers_path,
+                                                    multiarch_tuple,
+                                                    &abi->libdl_platform_error);
+
+  if (abi->libdl_platform_error != NULL && error != NULL)
+    *error = g_error_copy (abi->libdl_platform_error);
+
+  return g_strdup (abi->libdl_platform);
+}
diff --git a/steam-runtime-tools/system-info.h b/steam-runtime-tools/system-info.h
index 387ed20815cb95fe669438f7f1d208d52f89db37..5b3c91a1320c6b3be826ecd851f1151c0d721531 100644
--- a/steam-runtime-tools/system-info.h
+++ b/steam-runtime-tools/system-info.h
@@ -288,6 +288,15 @@ gboolean srt_system_info_check_runtime_linker (SrtSystemInfo *self,
                                                gchar **real_path,
                                                GError **error);
 
+_SRT_PUBLIC
+gchar *srt_system_info_dup_libdl_lib (SrtSystemInfo *self,
+                                      const char *multiarch_tuple,
+                                      GError **error);
+_SRT_PUBLIC
+gchar *srt_system_info_dup_libdl_platform (SrtSystemInfo *self,
+                                           const char *multiarch_tuple,
+                                           GError **error);
+
 #ifdef G_DEFINE_AUTOPTR_CLEANUP_FUNC
 G_DEFINE_AUTOPTR_CLEANUP_FUNC (SrtSystemInfo, g_object_unref)
 #endif
diff --git a/tests/json-report/full-good-report.json b/tests/json-report/full-good-report.json
index d12799f1e9714fe354508ff860a4cd1c218dafc8..985a3eb9363a7dad76ade3a38b8e978ebccd7dea 100644
--- a/tests/json-report/full-good-report.json
+++ b/tests/json-report/full-good-report.json
@@ -53,6 +53,12 @@
   "architectures" : {
     "i386-linux-gnu" : {
       "can-run" : false,
+      "libdl-LIB" : "lib32",
+      "libdl-PLATFORM" : {
+        "error-domain" : "g-io-error-quark",
+        "error-code" : 1,
+        "error" : "Unable to find the library: ${ORIGIN}/i386-linux-gnu/${PLATFORM}/libidentify-platform.so: cannot open shared object file: No such file or directory"
+      },
       "runtime-linker": {
         "path" : "/lib/ld-linux.so.2",
         "error-domain" : "g-io-error-quark",
@@ -130,6 +136,8 @@
     },
     "x86_64-linux-gnu" : {
       "can-run" : true,
+      "libdl-LIB" : "lib",
+      "libdl-PLATFORM" : "x86_64",
       "runtime-linker": {
         "path" : "/lib64/ld-linux-x86-64.so.2",
         "resolved" : "/lib/x86_64-linux-gnu/ld-2.31.so"