diff --git a/steam-runtime-tools/utils-internal.h b/steam-runtime-tools/utils-internal.h
index 308e60a1b9ec084a34820ab3d0ae549ae775e423..ca7f38f49ceb47b8de8cc2998003cbc4075e363c 100644
--- a/steam-runtime-tools/utils-internal.h
+++ b/steam-runtime-tools/utils-internal.h
@@ -87,3 +87,10 @@ gchar ** _srt_json_array_to_strv (JsonObject *json_obj,
 gboolean _srt_rm_rf (const char *directory);
 
 FILE *_srt_divert_stdout_to_stderr (GError **error);
+
+G_GNUC_INTERNAL
+gboolean _srt_file_get_contents_in_sysroot (int sysroot_fd,
+                                            const char *path,
+                                            gchar **contents,
+                                            gsize *len,
+                                            GError **error);
diff --git a/steam-runtime-tools/utils.c b/steam-runtime-tools/utils.c
index cd059711b13c33d3820ee7a33a99befa83287921..55d2917ed778b91146193cd460d4e4182d498080 100644
--- a/steam-runtime-tools/utils.c
+++ b/steam-runtime-tools/utils.c
@@ -44,6 +44,7 @@
 #include <glib-object.h>
 #include <gio/gio.h>
 #include "steam-runtime-tools/glib-backports-internal.h"
+#include "steam-runtime-tools/resolve-in-sysroot-internal.h"
 
 #ifdef HAVE_GETAUXVAL
 #define getauxval_AT_SECURE() getauxval (AT_SECURE)
@@ -862,3 +863,57 @@ _srt_divert_stdout_to_stderr (GError **error)
 
   return g_steal_pointer (&original_stdout);
 }
+
+/*
+ * _srt_file_get_contents_in_sysroot:
+ * @sysroot_fd: A directory fd opened on the sysroot or `/`
+ * @path: Absolute or root-relative path within @sysroot_fd
+ * @contents: (out) (not optional): Used to return contents
+ * @len: (out) (optional): Used to return length
+ * @error: Used to return error on failure
+ *
+ * Like g_file_get_contents(), but the file is in a sysroot, and we
+ * follow symlinks as though @sysroot_fd was the root directory
+ * (similar to `fakechroot`).
+ *
+ * Returns: %TRUE if successful
+ */
+gboolean
+_srt_file_get_contents_in_sysroot (int sysroot_fd,
+                                   const char *path,
+                                   gchar **contents,
+                                   gsize *len,
+                                   GError **error)
+{
+  g_autofree gchar *real_path = NULL;
+  g_autofree gchar *fd_path = NULL;
+  g_autofree gchar *ignored = NULL;
+  glnx_autofd int fd = -1;
+
+  g_return_val_if_fail (sysroot_fd >= 0, FALSE);
+  g_return_val_if_fail (path != NULL, FALSE);
+  g_return_val_if_fail (contents != NULL, FALSE);
+  g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+  if (contents == NULL)
+    contents = &ignored;
+
+  fd = _srt_resolve_in_sysroot (sysroot_fd,
+                                path,
+                                SRT_RESOLVE_FLAGS_READABLE,
+                                &real_path,
+                                error);
+
+  if (fd < 0)
+    return FALSE;
+
+  fd_path = g_strdup_printf ("/proc/self/fd/%d", fd);
+
+  if (!g_file_get_contents (fd_path, contents, len, error))
+    {
+      g_prefix_error (error, "Unable to read %s: ", real_path);
+      return FALSE;
+    }
+
+  return TRUE;
+}