diff --git a/bin/meson.build b/bin/meson.build
index da4f5b95032bb960af383ab58f4c98db2af90c65..8761bde2f682be618c6971921dc426b9311cda61 100644
--- a/bin/meson.build
+++ b/bin/meson.build
@@ -26,6 +26,10 @@ executable(
   'system-info.c',
   dependencies : [glib, libsteamrt_dep, json_glib],
   install : true,
+  # Use the adjacent libsteam-runtime-tools and json-glib, ignoring
+  # LD_LIBRARY_PATH even if set
+  build_rpath : bin_rpath,
+  install_rpath : bin_rpath,
 )
 
 if get_option('man')
diff --git a/debian/rules b/debian/rules
index f419379e51aa90f684cc8a05ff45ca1885276b0f..9e30a9ef675c0a9099c270fcb9f53adcf840319e 100755
--- a/debian/rules
+++ b/debian/rules
@@ -38,6 +38,7 @@ override_dh_auto_configure:
 	meson builddir \
 		--prefix=/usr \
 		--libexecdir=/usr/libexec \
+		--libdir=lib/$(DEB_HOST_MULTIARCH) \
 		-Dgtk_doc=$(gtk_doc_has_cflags) \
 		-Dintrospection=false \
 		-Dmultiarch_tuple=$(DEB_HOST_MULTIARCH) \
diff --git a/helpers/meson.build b/helpers/meson.build
index 3c37a53a09cd5589c26c7e31bc4ac65f7fdf557c..f0f475a59f6ea2885145506460e9d605ef84bb5d 100644
--- a/helpers/meson.build
+++ b/helpers/meson.build
@@ -26,6 +26,8 @@ executable(
   'true.c',
   install : true,
   install_dir : pkglibexecdir,
+  # Deliberately no RPATH/RUNPATH here: it's unnecessary because this
+  # executable has no dependencies.
 )
 
 executable(
@@ -35,6 +37,9 @@ executable(
   dependencies : [json_glib, glib],
   install : true,
   install_dir : pkglibexecdir,
+  # Use json-glib and GLib from the adjacent libdir, ignoring LD_LIBRARY_PATH
+  build_rpath : pkglibexec_rpath,
+  install_rpath : pkglibexec_rpath,
 )
 
 executable(
@@ -43,6 +48,8 @@ executable(
   dependencies : [libdl],
   install : true,
   install_dir : pkglibexecdir,
+  # Deliberately no RPATH/RUNPATH here: we want to determine whether
+  # a game with no special linking would be able to load a library.
 )
 
 executable(
@@ -54,6 +61,9 @@ executable(
     get_option('libexecdir'),
     'steam-runtime-tools-' + api_major,
   )
+  # Deliberately no RPATH/RUNPATH here: we want to determine whether
+  # a game with no special linking would be able to use libvulkan
+  # and its associated drivers successfully.
 ) 
 
 custom_target(
diff --git a/meson.build b/meson.build
index 046d480fc378a232b9a778e115053032ae19d6df..acc59c3f82b29e028c9d2c7c9ab8d466edeb0e8c 100644
--- a/meson.build
+++ b/meson.build
@@ -100,6 +100,19 @@ c_compiler = meson.get_compiler('c')
 supported_warning_cflags = c_compiler.get_supported_arguments(warning_cflags)
 add_project_arguments(supported_warning_cflags, language : 'c')
 
+add_project_arguments(
+  c_compiler.get_supported_arguments(
+    '-Wl,-z,origin',
+    # Generate RPATH in preference to RUNPATH, even if RUNPATH is the
+    # linker's default. We want RPATH here, because it's used recursively
+    # for both direct and indirect dependencies: in particular we need
+    # to pick up the whole GLib family (GLib, GObject, GIO) from the same
+    # location. (It also bypasses LD_LIBRARY_PATH.)
+    '-Wl,-z,--disable-new-dtags',
+  ),
+  language : 'c',
+)
+
 foreach flag : no_warning_cflags
   supported_no_warning_cflags = c_compiler.get_supported_arguments([
     '-Wno-error=' + flag,
@@ -150,10 +163,28 @@ libdl = c_compiler.find_library('dl', required : false)
 
 project_include_dirs = include_directories('.')
 
+# RPATH for executables installed in get_option('bindir').
+# This assumes that bindir is a single directory component (normally
+# 'bin'), so that ../ gets us from get_option('bindir') to the prefix,
+# and that libdir is relative.
+bin_rpath = '${ORIGIN}/../' + get_option('libdir')
+if get_option('prefix').endswith('/usr')
+  # Parts of the GLib family of libraries are in /lib
+  bin_rpath = bin_rpath + ':${ORIGIN}/../../' + get_option('libdir')
+endif
+
 pkglibexecdir = join_paths(
   get_option('libexecdir'),
   'steam-runtime-tools-' + api_major,
 )
+# RPATH for executables installed in pkglibexecdir.
+# This assumes that libexecdir is a single directory component (normally
+# 'libexec'), so that ../../ gets us from pkglibexecdir to the prefix,
+# and that libdir is relative
+pkglibexec_rpath = '${ORIGIN}/../../' + get_option('libdir')
+if get_option('prefix').endswith('/usr')
+  pkglibexec_rpath = pkglibexec_rpath + ':${ORIGIN}/../../../' + get_option('libdir')
+endif
 
 if get_option('multiarch_tuple') != ''
   multiarch = get_option('multiarch_tuple')
diff --git a/steam-runtime-tools/system-info.c b/steam-runtime-tools/system-info.c
index ae2144d218b4577faf222b1e283b01c85a89f1c5..603f6a74130c230c5439deae7b029b6f156c075e 100644
--- a/steam-runtime-tools/system-info.c
+++ b/steam-runtime-tools/system-info.c
@@ -581,6 +581,9 @@ get_environ (SrtSystemInfo *self)
     return environ;
 }
 
+/* Path components from ${prefix} to steamrt expectations */
+#define STEAMRT_EXPECTATIONS "lib", "steamrt", "expectations"
+
 static gboolean
 ensure_expectations (SrtSystemInfo *self)
 {
@@ -589,16 +592,26 @@ ensure_expectations (SrtSystemInfo *self)
   if (self->expectations == NULL)
     {
       const char *runtime;
-      const char *sysroot = "/";
-      gchar *def;
+      gchar *def = NULL;
 
       runtime = g_environ_getenv (get_environ (self), "STEAM_RUNTIME");
 
       if (runtime != NULL && runtime[0] == '/')
-        sysroot = runtime;
+        {
+          def = g_build_filename (runtime, "usr", STEAMRT_EXPECTATIONS,
+                                  NULL);
+        }
+
+      if (def == NULL)
+        {
+          runtime = _srt_find_myself (NULL, NULL);
+
+          if (runtime != NULL)
+            def = g_build_filename (runtime, STEAMRT_EXPECTATIONS, NULL);
+        }
 
-      def = g_build_filename (sysroot, "usr", "lib", "steamrt",
-                              "expectations", NULL);
+      if (def == NULL)
+        def = g_build_filename ("/usr", STEAMRT_EXPECTATIONS, NULL);
 
       if (g_file_test (def, G_FILE_TEST_IS_DIR))
         self->expectations = g_steal_pointer (&def);
diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h
index 8f6b15e2d08cdb263a1686781c82e4e5de797142..dc74724aec8959a6aaf8fedc5edab45647eb4089 100644
--- a/steam-runtime-tools/utils-internal.h
+++ b/steam-runtime-tools/utils-internal.h
@@ -42,3 +42,5 @@ G_GNUC_INTERNAL GPtrArray *_srt_get_helper (const char *helpers_path,
                                             SrtHelperFlags flags,
                                             GError **error);
 gchar *_srt_filter_gameoverlayrenderer (const gchar *input);
+G_GNUC_INTERNAL const char *_srt_find_myself (const char **helpers_path_out,
+                                              GError **error);
diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c
index 00d7346326f0f5d9045b778bccc3e612ebf91d25..c3bb136320ba45f832e832f03b0dd0ef7ffbdf91 100644
--- a/steam-runtime-tools/utils.c
+++ b/steam-runtime-tools/utils.c
@@ -146,40 +146,35 @@ _srt_check_not_setuid (void)
   return !is_setuid;
 }
 
-static gchar *global_helpers_path = NULL;
-
-static const char *
-_srt_get_helpers_path (GError **error)
+G_GNUC_INTERNAL const char *
+_srt_find_myself (const char **helpers_path_out,
+                  GError **error)
 {
-  const char *path;
+  static gchar *saved_prefix = NULL;
+  static gchar *saved_helpers_path = NULL;
   Dl_info ignored;
   struct link_map *map = NULL;
-  gchar *dir;
+  gchar *dir = NULL;
 
   g_return_val_if_fail (_srt_check_not_setuid (), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+  g_return_val_if_fail (helpers_path_out == NULL || *helpers_path_out == NULL,
+                        NULL);
 
-  path = global_helpers_path;
-
-  if (path != NULL)
-    goto out;
-
-  path = g_getenv ("SRT_HELPERS_PATH");
-
-  if (path != NULL)
+  if (saved_prefix != NULL && saved_helpers_path != NULL)
     goto out;
 
-  if (dladdr1 (_srt_get_helpers_path, &ignored, (void **) &map,
+  if (dladdr1 (_srt_find_myself, &ignored, (void **) &map,
                RTLD_DL_LINKMAP) == 0 ||
       map == NULL)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "Unable to locate shared library containing "
-                   "_srt_get_helpers_path()");
+                   "_srt_find_myself()");
       goto out;
     }
 
-  g_debug ("Found _srt_get_helpers_path() in %s", map->l_name);
+  g_debug ("Found _srt_find_myself() in %s", map->l_name);
   dir = g_path_get_dirname (map->l_name);
 
   if (g_str_has_suffix (dir, "/lib/" _SRT_MULTIARCH))
@@ -198,16 +193,18 @@ _srt_get_helpers_path (GError **error)
       dir = g_strdup ("/usr");
     }
 
+  saved_prefix = g_steal_pointer (&dir);
   /* deliberate one-per-process leak */
-  global_helpers_path = g_build_filename (
-      dir, "libexec", "steam-runtime-tools-" _SRT_API_MAJOR,
+  saved_helpers_path = g_build_filename (
+      saved_prefix, "libexec", "steam-runtime-tools-" _SRT_API_MAJOR,
       NULL);
-  path = global_helpers_path;
-
-  g_free (dir);
 
 out:
-  return path;
+  if (helpers_path_out != NULL)
+    *helpers_path_out = saved_helpers_path;
+
+  g_free (dir);
+  return saved_prefix;
 }
 
 /*
@@ -266,14 +263,13 @@ _srt_get_helper (const char *helpers_path,
     }
 
   if (helpers_path == NULL)
-    {
-      helpers_path = _srt_get_helpers_path (error);
+    helpers_path = g_getenv ("SRT_HELPERS_PATH");
 
-      if (helpers_path == NULL)
-        {
-          g_ptr_array_unref (argv);
-          return NULL;
-        }
+  if (helpers_path == NULL
+      && _srt_find_myself (&helpers_path, error) == NULL)
+    {
+      g_ptr_array_unref (argv);
+      return NULL;
     }
 
   /* Prefer a helper from ${SRT_HELPERS_PATH} or
diff --git a/tests/valgrind.supp b/tests/valgrind.supp
index b6664283c14350df6561c8ceb790f9c41f110135..835990f895fade5a9903e2faa2592ce07b2515a1 100644
--- a/tests/valgrind.supp
+++ b/tests/valgrind.supp
@@ -4,7 +4,7 @@
    Memcheck:Leak
    ...
    fun:g_build_filename
-   fun:_srt_get_helpers_path
+   fun:_srt_find_myself
 }
 
 # Dependencies