diff --git a/plug-ins/print/print.c b/plug-ins/print/print.c index b3d69ad790264a552f948bb089f7d6b3468761d1..6c4913b8f28349ebb73b217d938fee4160b8b5c2 100644 --- a/plug-ins/print/print.c +++ b/plug-ins/print/print.c @@ -17,6 +17,15 @@ #include "config.h" +#ifdef __linux__ +#define _GNU_SOURCE +#ifdef __pic__ +#include +#include +#include +#endif +#endif + #include #include @@ -253,6 +262,120 @@ print_run (GimpProcedure *procedure, return gimp_procedure_new_return_values (procedure, status, error); } +#ifdef __linux__ +#ifdef __pic__ +static typeof (g_file_test) **libgtk_g_file_test_addr_addr; + +static int +fake_g_file_test (const char *filename, + GFileTest test) +{ + *libgtk_g_file_test_addr_addr = g_file_test; + char *expected_filename = g_build_filename (g_get_user_runtime_dir (), + "flatpak-info", + NULL); + int result = 0; + if (test != G_FILE_TEST_EXISTS || strcmp (filename, expected_filename)) + { + fprintf (stderr, + "print plug-in unstable anti-portal workaround failed because" + " libgtk-3.so.0 called g_file_test(\"%s\", %d)" + " instead of g_file_test(\"%s\", %d)\n", + filename, + test, + expected_filename, + G_FILE_TEST_EXISTS); + result = g_file_test (filename, test); + } + g_free (expected_filename); + return result; +} + +static void +unstable_anti_portal_workaround (void) +{ + const char *env_var = getenv ("GIMP_PRINT_UNSTABLE_ANTI_PORTAL_WORKAROUND"); + if (!(env_var && env_var[0] == '1' && env_var[1] == 0)) + return; + fputs ("environment variable GIMP_PRINT_UNSTABLE_ANTI_PORTAL_WORKAROUND=1" + " exists, trying unstable anti-portal workaround\n" + "if print plug-in crashes," + " try without GIMP_PRINT_UNSTABLE_ANTI_PORTAL_WORKAROUND\n", + stderr); + void *libgtk_handle = dlopen ("libgtk-3.so.0", RTLD_NOW | RTLD_NOLOAD); + if (!libgtk_handle) + { + fprintf (stderr, + "print plug-in unstable anti-portal workaround failed because" + " dlopen(\"libgtk-3.so.0\", RTLD_NOW | RTLD_NOLOAD)" + " failed because %s\n", + dlerror ()); + return; + } + const struct link_map *libgtk_link_map_addr; + if (dlinfo (libgtk_handle, RTLD_DI_LINKMAP, &libgtk_link_map_addr)) + { + fprintf (stderr, + "print plug-in unstable anti-portal workaround failed because" + " dlinfo(%p, RTLD_DI_LINKMAP, %p)" + " failed because %s\n", + libgtk_handle, + &libgtk_link_map_addr, + dlerror ()); + return; + } + const ElfW (Dyn) *dynamic_entry_addr = libgtk_link_map_addr->l_ld; + while (DT_PLTGOT != dynamic_entry_addr->d_tag) + { + if (DT_NULL == dynamic_entry_addr->d_tag) + { + fputs ("print plug-in unstable anti-portal workaround failed because" + " couldn't find DT_PLTGOT dynamic entry in libgtk-3.so.0\n", + stderr); + return; + } + ++dynamic_entry_addr; + } + typeof (g_file_test) **addr_addr = (typeof (g_file_test) **) dynamic_entry_addr->d_un.d_ptr; + /* reasons why loop below crashes or reads garbage: + * - libgtk-3.so.0 doesn't depend on g_file_test + * - libgtk-3.so.0 is not compiled with -z now + * - libgtk-3.so.0 is compiled with -z rodynamic + * -z {something} documented in https://github.com/rui314/mold/blob/main/docs/mold.md#gnu-compatible-options + */ + while (*addr_addr != g_file_test) + ++addr_addr; + /* addr_addr_page is addr_addr rounded down to multiple of page_size + * (assuming page_size is power of 2) + */ + unsigned page_size = getpagesize (); + void *addr_addr_page = (void *) ((size_t) addr_addr & -(size_t) page_size); + if (mprotect (addr_addr_page, page_size, PROT_READ | PROT_WRITE)) + { + fprintf (stderr, + "print plug-in unstable anti-portal workaround failed because" + " mprotect(%p, %u, PROT_READ | PROT_WRITE)" + " failed because %m\n", + addr_addr_page, + page_size); + return; + } + libgtk_g_file_test_addr_addr = addr_addr; + *addr_addr = fake_g_file_test; +} +#else /* #ifdef __pic__ */ +static void +unstable_anti_portal_workaround (void) +{ + const char *env_var = getenv ("GIMP_PRINT_UNSTABLE_ANTI_PORTAL_WORKAROUND"); + if (env_var && env_var[0] == '1' && env_var[1] == 0) + fputs ("environment variable GIMP_PRINT_UNSTABLE_ANTI_PORTAL_WORKAROUND=1" + " exists, but print plug-in is not compiled with -fpic or -fpie\n", + stderr); +} +#endif /* #ifdef __pic__, #else */ +#endif /* #ifdef __linux__ */ + static GimpPDBStatusType print_image (GimpImage *image, gboolean interactive, @@ -328,6 +451,9 @@ print_image (GimpImage *image, gtk_print_operation_set_embed_page_setup (operation, TRUE); #endif +#ifdef __linux__ + unstable_anti_portal_workaround (); +#endif result = gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, NULL, error); @@ -340,6 +466,9 @@ print_image (GimpImage *image, } else { +#ifdef __linux__ + unstable_anti_portal_workaround (); +#endif result = gtk_print_operation_run (operation, GTK_PRINT_OPERATION_ACTION_PRINT, NULL, error);