diff --git a/pressure-vessel/adverb-preload.c b/pressure-vessel/adverb-preload.c index 2685f6fbfad45f9298ea1ca3da8ec81c6cd411ca..f66ea90d9160bf1e20171dfded46f896d18d66a2 100644 --- a/pressure-vessel/adverb-preload.c +++ b/pressure-vessel/adverb-preload.c @@ -49,119 +49,128 @@ append_to_search_path (const gchar *item, GString *search_path) pv_search_path_append (search_path, item); } -gboolean -pv_adverb_set_up_preload_modules (FlatpakBwrap *wrapped_command, - PvPerArchDirs *lib_temp_dirs, - const PvAdverbPreloadModule *preload_modules, - gsize n_preload_modules, - GError **error) +static void +do_one_preload_module (const PvAdverbPreloadModule *module, + GPtrArray *search_path, + PvPerArchDirs *lib_temp_dirs) { - GPtrArray *preload_search_paths[G_N_ELEMENTS (pv_preload_variables)] = { NULL }; - gsize i; + const char *preload = module->name; + const char *base; + gsize abi_index = module->abi_index; - /* Iterate through all modules, populating preload_search_paths */ - for (i = 0; i < n_preload_modules; i++) - { - const PvAdverbPreloadModule *module = preload_modules + i; - GPtrArray *search_path = preload_search_paths[module->index_in_preload_variables]; - const char *preload = module->name; - const char *base; - gsize abi_index = module->abi_index; + g_assert (preload != NULL); - g_assert (preload != NULL); + if (*preload == '\0') + return; - if (*preload == '\0') - continue; + base = glnx_basename (preload); - base = glnx_basename (preload); + /* If we were not able to create the temporary library + * directories, we simply avoid any adjustment and try to continue */ + if (lib_temp_dirs == NULL) + { + g_ptr_array_add (search_path, g_strdup (preload)); + return; + } - if (search_path == NULL) + if (abi_index == PV_UNSPECIFIED_ABI + && module->index_in_preload_variables == PV_PRELOAD_VARIABLE_INDEX_LD_PRELOAD + && strcmp (base, "gameoverlayrenderer.so") == 0) + { + for (gsize abi = 0; abi < PV_N_SUPPORTED_ARCHITECTURES; abi++) { - preload_search_paths[module->index_in_preload_variables] - = search_path - = g_ptr_array_new_full (n_preload_modules, g_free); + g_autofree gchar *expected_suffix = g_strdup_printf ("/%s/gameoverlayrenderer.so", + pv_multiarch_details[abi].gameoverlayrenderer_dir); + + if (g_str_has_suffix (preload, expected_suffix)) + { + abi_index = abi; + break; + } } - /* If we were not able to create the temporary library - * directories, we simply avoid any adjustment and try to continue */ - if (lib_temp_dirs == NULL) + if (abi_index == PV_UNSPECIFIED_ABI) { - g_ptr_array_add (search_path, g_strdup (preload)); - continue; + g_debug ("Preloading %s from an unexpected path \"%s\", " + "just leave it as is without adjusting", + base, preload); } + } - if (abi_index == PV_UNSPECIFIED_ABI - && module->index_in_preload_variables == PV_PRELOAD_VARIABLE_INDEX_LD_PRELOAD - && strcmp (base, "gameoverlayrenderer.so") == 0) - { - for (gsize abi = 0; abi < PV_N_SUPPORTED_ARCHITECTURES; abi++) - { - g_autofree gchar *expected_suffix = g_strdup_printf ("/%s/gameoverlayrenderer.so", - pv_multiarch_details[abi].gameoverlayrenderer_dir); + if (abi_index != PV_UNSPECIFIED_ABI) + { + g_autofree gchar *link = NULL; + g_autofree gchar *platform_path = NULL; + int saved_errno; - if (g_str_has_suffix (preload, expected_suffix)) - { - abi_index = abi; - break; - } - } + g_debug ("Module %s is for %s", + preload, pv_multiarch_details[abi_index].tuple); + platform_path = g_build_filename (lib_temp_dirs->libdl_token_path, + base, NULL); + link = g_build_filename (lib_temp_dirs->abi_paths[abi_index], + base, NULL); - if (abi_index == PV_UNSPECIFIED_ABI) - { - g_debug ("Preloading %s from an unexpected path \"%s\", " - "just leave it as is without adjusting", - base, preload); - } + if (symlink (preload, link) == 0) + { + g_debug ("created symlink %s -> %s", link, preload); + ptr_array_add_unique (search_path, platform_path, + g_str_equal, generic_strdup); + return; } - if (abi_index != PV_UNSPECIFIED_ABI) + saved_errno = errno; + + if (saved_errno == EEXIST) { - g_autofree gchar *link = NULL; - g_autofree gchar *platform_path = NULL; - int saved_errno; - - g_debug ("Module %s is for %s", - preload, pv_multiarch_details[abi_index].tuple); - platform_path = g_build_filename (lib_temp_dirs->libdl_token_path, - base, NULL); - link = g_build_filename (lib_temp_dirs->abi_paths[abi_index], - base, NULL); - - if (symlink (preload, link) == 0) + g_autofree gchar *found = NULL; + + found = glnx_readlinkat_malloc (-1, link, NULL, NULL); + + if (g_strcmp0 (found, preload) == 0) { - g_debug ("created symlink %s -> %s", link, preload); + g_debug ("Already created symlink %s -> %s", link, preload); ptr_array_add_unique (search_path, platform_path, g_str_equal, generic_strdup); - continue; + return; } + } - saved_errno = errno; - - if (saved_errno == EEXIST) - { - g_autofree gchar *found = NULL; + /* Use the object as-is instead of trying to do anything clever */ + g_debug ("Unable to create symlink %s -> %s: %s", + link, preload, g_strerror (saved_errno)); + g_ptr_array_add (search_path, g_strdup (preload)); + } + else + { + g_debug ("Module %s is for all architectures", preload); + g_ptr_array_add (search_path, g_strdup (preload)); + } +} - found = glnx_readlinkat_malloc (-1, link, NULL, NULL); +gboolean +pv_adverb_set_up_preload_modules (FlatpakBwrap *wrapped_command, + PvPerArchDirs *lib_temp_dirs, + const PvAdverbPreloadModule *preload_modules, + gsize n_preload_modules, + GError **error) +{ + GPtrArray *preload_search_paths[G_N_ELEMENTS (pv_preload_variables)] = { NULL }; + gsize i; - if (g_strcmp0 (found, preload) == 0) - { - g_debug ("Already created symlink %s -> %s", link, preload); - ptr_array_add_unique (search_path, platform_path, - g_str_equal, generic_strdup); - continue; - } - } + /* Iterate through all modules, populating preload_search_paths */ + for (i = 0; i < n_preload_modules; i++) + { + const PvAdverbPreloadModule *module = preload_modules + i; + GPtrArray *search_path = preload_search_paths[module->index_in_preload_variables]; - /* Use the object as-is instead of trying to do anything clever */ - g_debug ("Unable to create symlink %s -> %s: %s", - link, preload, g_strerror (saved_errno)); - g_ptr_array_add (search_path, g_strdup (preload)); - } - else + if (search_path == NULL) { - g_debug ("Module %s is for all architectures", preload); - g_ptr_array_add (search_path, g_strdup (preload)); + preload_search_paths[module->index_in_preload_variables] + = search_path + = g_ptr_array_new_full (n_preload_modules, g_free); } + + do_one_preload_module (module, search_path, lib_temp_dirs); } /* Serialize search_paths[PRELOAD_VARIABLE_INDEX_LD_AUDIT] into @@ -183,3 +192,107 @@ pv_adverb_set_up_preload_modules (FlatpakBwrap *wrapped_command, return TRUE; } + +/* + * pv_adverb_preload_module_parse_adverb_cli: + * @self: The module, which must be initialized + * to %PV_ADVERB_PRELOAD_MODULE_INIT + * @option: The command-line option that we are parsing, for + * example `--ld-preload` + * @which: The corresponding type of module + * @value: The argument to the command-line option + * @error: Used to report an error if unsuccessful + * + * Parse the `--ld-preload` or `--ld-audit` option of `pv-adverb`. + * + * Returns: %TRUE if the argument was parsed successfully + */ +gboolean +pv_adverb_preload_module_parse_adverb_cli (PvAdverbPreloadModule *self, + const char *option, + PvPreloadVariableIndex which, + const char *value, + GError **error) +{ + g_auto(GStrv) parts = NULL; + const char *architecture = NULL; + gsize abi_index = PV_UNSPECIFIED_ABI; + gsize abi; + + g_return_val_if_fail (self->name == NULL, FALSE); + g_return_val_if_fail (self->abi_index == PV_UNSPECIFIED_ABI, FALSE); + + parts = g_strsplit (value, ":", 0); + + if (parts[0] != NULL) + { + gsize i; + + for (i = 1; parts[i] != NULL; i++) + { + if (g_str_has_prefix (parts[i], "abi=")) + { + architecture = parts[i] + strlen ("abi="); + + for (abi = 0; abi < PV_N_SUPPORTED_ARCHITECTURES; abi++) + { + if (strcmp (architecture, pv_multiarch_tuples[abi]) == 0) + { + abi_index = abi; + break; + } + } + + if (abi_index == PV_UNSPECIFIED_ABI) + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Unsupported ABI %s", + architecture); + return FALSE; + } + + continue; + } + else + { + g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, + "Unexpected option in %s=\"%s\": %s", + option, value, parts[i]); + return FALSE; + } + } + + value = parts[0]; + } + + self->index_in_preload_variables = which; + self->name = g_strdup (value); + self->abi_index = abi_index; + return TRUE; +} + +gchar * +pv_adverb_preload_module_to_adverb_cli (PvAdverbPreloadModule *self) +{ + g_autoptr(GString) buf = NULL; + const PvPreloadVariable *variable; + + g_return_val_if_fail (self->index_in_preload_variables < G_N_ELEMENTS (pv_preload_variables), NULL); + g_return_val_if_fail (self->name != NULL, NULL); + g_return_val_if_fail (self->abi_index == PV_UNSPECIFIED_ABI + || self->abi_index < PV_N_SUPPORTED_ARCHITECTURES, + NULL); + + variable = &pv_preload_variables[self->index_in_preload_variables]; + buf = g_string_new (variable->adverb_option); + g_string_append_c (buf, '='); + g_string_append (buf, self->name); + + if (self->abi_index != PV_UNSPECIFIED_ABI) + { + g_string_append (buf, ":abi="); + g_string_append (buf, pv_multiarch_tuples[self->abi_index]); + } + + return g_string_free (g_steal_pointer (&buf), FALSE); +} diff --git a/pressure-vessel/adverb-preload.h b/pressure-vessel/adverb-preload.h index bd9c578dcfb66ac6c30804e1136551fbd2b05fed..4c79b4f69f514f67fccc5d1e0d1d417c8352e4d2 100644 --- a/pressure-vessel/adverb-preload.h +++ b/pressure-vessel/adverb-preload.h @@ -56,6 +56,14 @@ pv_adverb_preload_module_clear (gpointer p) G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (PvAdverbPreloadModule, pv_adverb_preload_module_clear) +gchar *pv_adverb_preload_module_to_adverb_cli (PvAdverbPreloadModule *self); + +gboolean pv_adverb_preload_module_parse_adverb_cli (PvAdverbPreloadModule *self, + const char *option, + PvPreloadVariableIndex which, + const char *value, + GError **error); + gboolean pv_adverb_set_up_preload_modules (FlatpakBwrap *wrapped_command, PvPerArchDirs *lib_temp_dirs, const PvAdverbPreloadModule *preload_modules, diff --git a/pressure-vessel/adverb.c b/pressure-vessel/adverb.c index bd9499d0c741cc6721e04d94e85f685164a4952b..e2f99497edcd857204bd91ed09d13399ab41575d 100644 --- a/pressure-vessel/adverb.c +++ b/pressure-vessel/adverb.c @@ -128,52 +128,14 @@ opt_ld_something (const char *option, gpointer data, GError **error) { - PvAdverbPreloadModule module = { NULL, 0, PV_UNSPECIFIED_ABI }; - g_auto(GStrv) parts = NULL; - const char *architecture = NULL; + PvAdverbPreloadModule module = PV_ADVERB_PRELOAD_MODULE_INIT; - parts = g_strsplit (value, ":", 0); - - if (parts[0] != NULL) - { - gsize i; - - for (i = 1; parts[i] != NULL; i++) - { - if (g_str_has_prefix (parts[i], "abi=")) - { - gsize abi; - - architecture = parts[i] + strlen ("abi="); - - for (abi = 0; abi < PV_N_SUPPORTED_ARCHITECTURES; abi++) - { - if (strcmp (architecture, pv_multiarch_details[abi].tuple) == 0) - { - module.abi_index = abi; - break; - } - } - - if (module.abi_index == PV_UNSPECIFIED_ABI) - { - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "Unsupported ABI %s", - architecture); - return FALSE; - } - } - else - { - g_set_error (error, G_OPTION_ERROR, G_OPTION_ERROR_BAD_VALUE, - "Unexpected option in %s=\"%s\": %s", - option, value, parts[i]); - return FALSE; - } - } - - value = parts[0]; - } + if (!pv_adverb_preload_module_parse_adverb_cli (&module, + option, + index_in_preload_variables, + value, + error)) + return FALSE; if (opt_preload_modules == NULL) { @@ -181,8 +143,6 @@ opt_ld_something (const char *option, g_array_set_clear_func (opt_preload_modules, pv_adverb_preload_module_clear); } - module.index_in_preload_variables = index_in_preload_variables; - module.name = g_strdup (value); g_array_append_val (opt_preload_modules, module); return TRUE; } diff --git a/pressure-vessel/wrap-setup.c b/pressure-vessel/wrap-setup.c index 7e31e55cf54cfd8da2e95e0c4cbe4847f6c4d0e7..8570ea535a2ff6ffc8c58689c535c2c3a887b41d 100644 --- a/pressure-vessel/wrap-setup.c +++ b/pressure-vessel/wrap-setup.c @@ -547,8 +547,8 @@ pv_wrap_move_into_scope (const char *steam_app_id) static void append_preload_internal (GPtrArray *argv, - const char *option, - const char *multiarch_tuple, + PvPreloadVariableIndex which, + gsize abi_index, const char *export_path, const char *original_path, GStrv env, @@ -556,39 +556,28 @@ append_preload_internal (GPtrArray *argv, PvRuntime *runtime, FlatpakExports *exports) { + g_auto(PvAdverbPreloadModule) module = PV_ADVERB_PRELOAD_MODULE_INIT; + g_autofree gchar *arg = NULL; gboolean flatpak_subsandbox = ((flags & PV_APPEND_PRELOAD_FLAGS_FLATPAK_SUBSANDBOX) != 0); + module.index_in_preload_variables = which; + module.abi_index = abi_index; + if (runtime != NULL && (g_str_has_prefix (original_path, "/usr/") || g_str_has_prefix (original_path, "/lib") || (flatpak_subsandbox && g_str_has_prefix (original_path, "/app/")))) { - g_autofree gchar *adjusted_path = NULL; const char *target = flatpak_subsandbox ? "/run/parent" : "/run/host"; - adjusted_path = g_build_filename (target, original_path, NULL); - g_debug ("%s -> %s", original_path, adjusted_path); - - if (multiarch_tuple != NULL) - g_ptr_array_add (argv, g_strdup_printf ("%s=%s:abi=%s", - option, adjusted_path, - multiarch_tuple)); - else - g_ptr_array_add (argv, g_strdup_printf ("%s=%s", - option, adjusted_path)); + module.name = g_build_filename (target, original_path, NULL); + g_debug ("%s -> %s", original_path, module.name); } else { + module.name = g_strdup (original_path); g_debug ("%s -> unmodified", original_path); - if (multiarch_tuple != NULL) - g_ptr_array_add (argv, g_strdup_printf ("%s=%s:abi=%s", - option, original_path, - multiarch_tuple)); - else - g_ptr_array_add (argv, g_strdup_printf ("%s=%s", - option, original_path)); - if (exports != NULL && export_path != NULL && export_path[0] == '/') { const gchar *steam_path = g_environ_getenv (env, "STEAM_COMPAT_CLIENT_INSTALL_PATH"); @@ -609,6 +598,10 @@ append_preload_internal (GPtrArray *argv, } } } + + arg = pv_adverb_preload_module_to_adverb_cli (&module); + g_return_if_fail (arg != NULL); + g_ptr_array_add (argv, g_steal_pointer (&arg)); } /* @@ -628,7 +621,7 @@ append_preload_internal (GPtrArray *argv, */ static void append_preload_unsupported_token (GPtrArray *argv, - const char *option, + PvPreloadVariableIndex which, const char *preload, GStrv env, PvAppendPreloadFlags flags, @@ -681,8 +674,8 @@ append_preload_unsupported_token (GPtrArray *argv, } append_preload_internal (argv, - option, - NULL, + which, + PV_UNSPECIFIED_ABI, export_path, preload, env, @@ -703,7 +696,7 @@ append_preload_unsupported_token (GPtrArray *argv, */ static void append_preload_per_architecture (GPtrArray *argv, - const char *option, + PvPreloadVariableIndex which, const char *preload, GStrv env, PvAppendPreloadFlags flags, @@ -783,8 +776,8 @@ append_preload_per_architecture (GPtrArray *argv, g_debug ("Found %s version of %s at %s", multiarch_tuple, preload, path); append_preload_internal (argv, - option, - multiarch_tuple, + which, + i, path, path, env, @@ -802,7 +795,7 @@ append_preload_per_architecture (GPtrArray *argv, static void append_preload_basename (GPtrArray *argv, - const char *option, + PvPreloadVariableIndex which, const char *preload, GStrv env, PvAppendPreloadFlags flags, @@ -837,10 +830,11 @@ append_preload_basename (GPtrArray *argv, * appropriate). */ g_debug ("Found \"%s\" in runtime or graphics stack provider, " "passing %s through as-is", - preload, option); + preload, + pv_preload_variables[which].variable); append_preload_internal (argv, - option, - NULL, + which, + PV_UNSPECIFIED_ABI, NULL, preload, env, @@ -858,7 +852,7 @@ append_preload_basename (GPtrArray *argv, "splitting architectures", preload); append_preload_per_architecture (argv, - option, + which, preload, env, flags, @@ -897,7 +891,6 @@ pv_wrap_append_preload (GPtrArray *argv, SrtLoadableKind kind; SrtLoadableFlags loadable_flags; const char *variable; - const char *option; g_return_if_fail (argv != NULL); g_return_if_fail (preload != NULL); @@ -906,7 +899,6 @@ pv_wrap_append_preload (GPtrArray *argv, g_return_if_fail (which < G_N_ELEMENTS (pv_preload_variables)); variable = pv_preload_variables[which].variable; - option = pv_preload_variables[which].adverb_option; if (strstr (preload, "gtk3-nocsd") != NULL) { @@ -930,7 +922,7 @@ pv_wrap_append_preload (GPtrArray *argv, /* Basenames can't have dynamic string tokens. */ g_warn_if_fail ((loadable_flags & SRT_LOADABLE_FLAGS_DYNAMIC_TOKENS) == 0); append_preload_basename (argv, - option, + which, preload, env, flags, @@ -944,7 +936,7 @@ pv_wrap_append_preload (GPtrArray *argv, | SRT_LOADABLE_FLAGS_UNKNOWN_TOKENS)) { append_preload_unsupported_token (argv, - option, + which, preload, env, flags, @@ -956,7 +948,7 @@ pv_wrap_append_preload (GPtrArray *argv, g_debug ("Found $LIB or $PLATFORM in \"%s\", splitting architectures", preload); append_preload_per_architecture (argv, - option, + which, preload, env, flags, @@ -969,8 +961,8 @@ pv_wrap_append_preload (GPtrArray *argv, * assume that preload is a concrete filename */ g_warn_if_fail ((loadable_flags & SRT_LOADABLE_FLAGS_DYNAMIC_TOKENS) == 0); append_preload_internal (argv, - option, - NULL, + which, + PV_UNSPECIFIED_ABI, preload, preload, env, diff --git a/tests/pressure-vessel/adverb-preload.c b/tests/pressure-vessel/adverb-preload.c index a50fc2a01425fc83b0b9a6eda291d446a18343e9..3024a75957d6e31ee1a67410a1e8f0f48a1fcf6e 100644 --- a/tests/pressure-vessel/adverb-preload.c +++ b/tests/pressure-vessel/adverb-preload.c @@ -310,6 +310,147 @@ test_biarch (Fixture *f, #endif } +typedef struct +{ + const char *option; + PvAdverbPreloadModule expected; +} CommandLineTest; + +static void +test_cli (Fixture *f, + gconstpointer context) +{ + static const CommandLineTest tests[] = + { + { + .option = "", + .expected = { + .name = (char *) "", + .abi_index = PV_UNSPECIFIED_ABI, + }, + }, + { + .option = "libpreload.so", + .expected = { + .name = (char *) "libpreload.so", + .abi_index = PV_UNSPECIFIED_ABI, + }, + }, +#if defined(__x86_64__) || defined(__i386__) + { + .option = "/lib64/libpreload.so:abi=" SRT_ABI_X86_64, + .expected = { + .name = (char *) "/lib64/libpreload.so", + .abi_index = 0, + }, + }, + { + .option = "/lib32/libpreload.so:abi=" SRT_ABI_I386, + .expected = { + .name = (char *) "/lib32/libpreload.so", + .abi_index = 1, + }, + }, +#endif +#if defined(_SRT_MULTIARCH) + { + .option = "/tmp/libabi.so:abi=" _SRT_MULTIARCH, + .expected = { + .name = (char *) "/tmp/libabi.so", +#if defined(__i386__) + /* On i386, we treat x86_64 as the primary architecture */ + .abi_index = 1, +#else + .abi_index = 0, +#endif + }, + }, +#endif + { + .option = "/tmp/libabi.so:nonsense", + .expected = { + .name = NULL, + }, + }, + }; + gsize i; + + for (i = 0; i < G_N_ELEMENTS (tests); i++) + { + const CommandLineTest *test = &tests[i]; + /* There's no real difference between our handling of LD_AUDIT + * and LD_PRELOAD, so we alternate between testing them both */ + const PvPreloadVariableIndex which = i % 2; + const char *option = pv_preload_variables[which].adverb_option; + g_autoptr(GError) local_error = NULL; + g_auto(PvAdverbPreloadModule) actual = PV_ADVERB_PRELOAD_MODULE_INIT; + gboolean ret; + + ret = pv_adverb_preload_module_parse_adverb_cli (&actual, + option, + which, + test->option, + &local_error); + + if (ret) + { + const char *abi = "(unspecified)"; + + if (actual.abi_index != PV_UNSPECIFIED_ABI) + { + g_assert_cmpuint (actual.abi_index, >=, 0); + g_assert_cmpuint (actual.abi_index, <, PV_N_SUPPORTED_ARCHITECTURES); + abi = pv_multiarch_details[actual.abi_index].tuple; + } + + g_test_message ("\"%s\" -> \"%s\", abi=%s", + test->option, actual.name, abi); + } + else + { + g_test_message ("\"%s\" -> error \"%s\"", + test->option, local_error->message); + } + + if (test->expected.name == NULL) + { + g_assert_false (ret); + g_assert_null (actual.name); + g_assert_nonnull (local_error); + } + else + { + g_autofree gchar *serialized = NULL; + g_auto(PvAdverbPreloadModule) reparsed = PV_ADVERB_PRELOAD_MODULE_INIT; + gchar *equals; + + g_assert_no_error (local_error); + g_assert_true (ret); + g_assert_cmpstr (actual.name, ==, test->expected.name); + g_assert_cmpuint (actual.abi_index, ==, test->expected.abi_index); + g_assert_cmpuint (actual.index_in_preload_variables, ==, which); + + /* Check that it round-trips */ + serialized = pv_adverb_preload_module_to_adverb_cli (&actual); + g_assert_true (g_str_has_prefix (serialized, option)); + equals = serialized + strlen (option); + g_assert_cmpint (*equals, ==, '='); + *equals = '\0'; + + ret = pv_adverb_preload_module_parse_adverb_cli (&reparsed, + option, + which, + equals + 1, + &local_error); + g_assert_no_error (local_error); + g_assert_true (ret); + g_assert_cmpstr (reparsed.name, ==, test->expected.name); + g_assert_cmpuint (reparsed.abi_index, ==, test->expected.abi_index); + g_assert_cmpuint (reparsed.index_in_preload_variables, ==, which); + } + } +} + /* * There is a special case for gameoverlayrenderer.so: * pv-adverb --ld-preload=/.../ubuntu12_32/gameoverlayrenderer.so is @@ -706,5 +847,9 @@ main (int argc, add_test ("/gameoverlayrenderer", test_gameoverlayrenderer); add_test ("/repetition", test_repetition); + /* This one isn't affected by whether we have the PvPerArchDirs or not */ + g_test_add ("/cli", Fixture, NULL, + setup, test_cli, teardown); + return g_test_run (); }