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/control b/debian/control
index a22c249a291a897e36912983b1702c5c87f13cdf..6cb6000f2559ca8cdeba61094836f63c7a4c9a0e 100644
--- a/debian/control
+++ b/debian/control
@@ -8,6 +8,7 @@ Build-Depends:
  g++ (>= 4:4.8) | g++-4.8,
  glslang-tools,
  gtk-doc-tools <!nodoc>,
+ libcapsule-tools,
  libglib2.0-dev,
  libjson-glib-dev (>= 1.0),
  libtheora0 <!nocheck>,
diff --git a/debian/libsteam-runtime-tools-0-helpers.install b/debian/libsteam-runtime-tools-0-helpers.install
index 4d24bb6e601f5ae39446eb84d572b5f5947a1614..4f58c509f4b15622ce24b6c0b13e02fa2b63f0b7 100644
--- a/debian/libsteam-runtime-tools-0-helpers.install
+++ b/debian/libsteam-runtime-tools-0-helpers.install
@@ -1 +1,2 @@
+usr/lib/*/steam-runtime-tools-0/*
 usr/libexec/steam-runtime-tools-0/*
diff --git a/debian/rules b/debian/rules
index 506e949aad2cc4ba049bd86181db704c3f334d44..e8200469bc50201641ac386f46de5c2910c85f42 100755
--- a/debian/rules
+++ b/debian/rules
@@ -40,10 +40,17 @@ override_dh_auto_clean:
 	rm -fr builddir
 	rm -fr debian/locales
 
+libexecdir := libexec
+pkglibexecdir := $(libexecdir)/steam-runtime-tools-0
+libdir := lib/$(DEB_HOST_MULTIARCH)
+pkglibdir := $(libdir)/steam-runtime-tools-0
+DESTDIR := $(CURDIR)/debian/tmp
+
 override_dh_auto_configure:
 	meson builddir \
 		--prefix=/usr \
-		--libexecdir=/usr/libexec \
+		--libexecdir=$(libexecdir) \
+		--libdir=$(libdir) \
 		-Dgtk_doc=$(gtk_doc_has_cflags) \
 		-Dintrospection=false \
 		-Dmultiarch_tuple=$(DEB_HOST_MULTIARCH) \
@@ -61,19 +68,46 @@ ifeq ($(filter nocheck,$(DEB_BUILD_OPTIONS)),)
 endif
 
 override_dh_auto_install:
-	DESTDIR=$(CURDIR)/debian/tmp ninja -C builddir install
+	DESTDIR=$(DESTDIR) ninja -C builddir install
 	ln -s ../../bin/$(DEB_HOST_MULTIARCH)-vulkaninfo \
-		debian/tmp/usr/libexec/steam-runtime-tools-0/
+		$(DESTDIR)/usr/$(pkglibexecdir)/
 	ln -s ../../bin/$(DEB_HOST_MULTIARCH)-wflinfo \
-		debian/tmp/usr/libexec/steam-runtime-tools-0/
-	for x in debian/tmp/usr/libexec/installed-tests/steam-runtime-tools-0/*; do \
+		$(DESTDIR)/usr/$(pkglibexecdir)/
+	set -e; for x in $(DESTDIR)/usr/libexec/installed-tests/steam-runtime-tools-0/*; do \
 		if [ -f "$$x" ] \
 		&& cmp -s "$$x" \
-			debian/tmp/usr/libexec/steam-runtime-tools-0/$(DEB_HOST_MULTIARCH)-true; \
+			$(DESTDIR)/usr/libexec/steam-runtime-tools-0/$(DEB_HOST_MULTIARCH)-true; \
 		then \
-			ln -fns ../../steam-runtime-tools-0/$(DEB_HOST_MULTIARCH)-true "$$x"; \
+			ln -fnsv ../../steam-runtime-tools-0/$(DEB_HOST_MULTIARCH)-true "$$x"; \
 		fi; \
 	done
+	install -d $(DESTDIR)/usr/$(pkglibdir)
+	ln -fnsv ../libsteam-runtime-tools-0.so.0 $(DESTDIR)/usr/$(pkglibdir)/
+	LD_LIBRARY_PATH='$(DESTDIR)/usr/$(libdir)' \
+	$(DEB_HOST_MULTIARCH)-capsule-capture-libs \
+		--container / \
+		--dest $(DESTDIR)/usr/$(pkglibdir) \
+		--link-target / \
+		--no-glibc \
+		--provider / \
+		only-dependencies:path:$(DESTDIR)/usr/bin/steam-runtime-system-info \
+		only-dependencies:path:$(DESTDIR)/usr/$(pkglibexecdir)/$(DEB_HOST_MULTIARCH)-check-locale \
+		$(NULL)
+
+override_dh_link:
+	dh_link
+	# Make the links in $(pkglibdir) relative. This is contrary
+	# to Debian policy, but is what we need for the LD_LIBRARY_PATH
+	# Steam Runtime, where GLib is in /lib.
+	set -e; for link in debian/libsteam-runtime-tools-0-helpers/usr/$(pkglibdir)/lib*; do \
+		target="$$(readlink -v "$$link")"; \
+		echo "before: $$link -> $$target"; \
+		case "$$target" in \
+			(/*) \
+				ln -fnsv "../../../..$${target}" "$${link}"; \
+				;; \
+		esac; \
+	done
 
 override_dh_missing:
 	dh_missing --fail-missing
diff --git a/helpers/meson.build b/helpers/meson.build
index b709726c3b63982ff5aa16fed05d6385b068f454..65eed6ed89972bc232c8e25d2b89f523ebdd3dfb 100644
--- a/helpers/meson.build
+++ b/helpers/meson.build
@@ -27,6 +27,8 @@ executable(
   include_directories : project_include_dirs,
   install : true,
   install_dir : pkglibexecdir,
+  # Deliberately no RPATH/RUNPATH here: it's unnecessary because this
+  # executable has no dependencies.
 )
 
 executable(
@@ -36,6 +38,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(
@@ -45,6 +50,8 @@ executable(
   include_directories : project_include_dirs,
   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(
@@ -57,7 +64,10 @@ 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(
   'frag.spv',
diff --git a/meson.build b/meson.build
index a70c8dd9aac9bbbaf17de2e7e8b6a8751ec65cc2..13bd831d069b59cc8b43635a58ff8358a51e1d9b 100644
--- a/meson.build
+++ b/meson.build
@@ -134,6 +134,19 @@ foreach flag : no_warning_cxxflags
   add_project_arguments(supported_no_warning_cxxflags, language : 'cpp')
 endforeach
 
+add_project_link_arguments(
+  c_compiler.get_supported_link_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',
+)
+
 conf_data = configuration_data()
 conf_data.set('_GNU_SOURCE', '1')
 conf_data.set_quoted('VERSION', meson.project_version())
@@ -181,11 +194,30 @@ libdl = c_compiler.find_library('dl', required : false)
 
 project_include_dirs = include_directories('.')
 
+# Relative path from get_option('prefix') to our private library directory
+pkglibdir = join_paths(
+  get_option('libdir'),
+  'steam-runtime-tools-' + api_major,
+)
+
+# 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}/../' + pkglibdir
+
+# Relative path from get_option('prefix') to our private libexec directory
 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}/../../' + pkglibdir
+
 if get_option('multiarch_tuple') != ''
   multiarch = get_option('multiarch_tuple')
 elif host_machine.cpu_family() == 'x86_64'
diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c
index c3bb136320ba45f832e832f03b0dd0ef7ffbdf91..1c2b95bcd2370906b3f8a3669469e158fa7bb255 100644
--- a/steam-runtime-tools/utils.c
+++ b/steam-runtime-tools/utils.c
@@ -146,6 +146,11 @@ _srt_check_not_setuid (void)
   return !is_setuid;
 }
 
+#define MULTIARCH_LIBDIR \
+  "/lib/" _SRT_MULTIARCH
+#define RELOCATABLE_PKGLIBDIR \
+  MULTIARCH_LIBDIR "/steam-runtime-tools-" _SRT_API_MAJOR
+
 G_GNUC_INTERNAL const char *
 _srt_find_myself (const char **helpers_path_out,
                   GError **error)
@@ -177,8 +182,10 @@ _srt_find_myself (const char **helpers_path_out,
   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))
-    dir[strlen (dir) - strlen ("/lib/" _SRT_MULTIARCH)] = '\0';
+  if (g_str_has_suffix (dir, RELOCATABLE_PKGLIBDIR))
+    dir[strlen (dir) - strlen (RELOCATABLE_PKGLIBDIR)] = '\0';
+  else if (g_str_has_suffix (dir, MULTIARCH_LIBDIR))
+    dir[strlen (dir) - strlen (MULTIARCH_LIBDIR)] = '\0';
   else if (g_str_has_suffix (dir, "/lib64"))
     dir[strlen (dir) - strlen ("/lib64")] = '\0';
   else if (g_str_has_suffix (dir, "/lib"))