From 41f8bbd02d3fdcd75899ff37954d993509a76c68 Mon Sep 17 00:00:00 2001 From: Loic Le Page Date: Wed, 5 Jan 2022 18:06:01 +0100 Subject: [PATCH 1/4] Fix process spawning with static build on Windows On Windows, process spawning needs an external helper exe which is found relatively to the glib DLL file. If glib has been built statically this file doesn't exist anymore and reference path is not the DLL path anymore but the current executable path. This patch searches for the helper exe taking as starting point the current executable path, relative 'bin', 'lib', 'glib' and 'gio' folders and then gets one level up until the root path. If this search doesn't give result then the helper exe is searched using the PATH variable. --- glib/glib-init.h | 2 + glib/glib-private.c | 1 + glib/glib-private.h | 5 +- glib/gspawn-win32.c | 16 ++---- glib/gutils.c | 41 --------------- glib/gwin32.c | 119 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 130 insertions(+), 54 deletions(-) diff --git a/glib/glib-init.h b/glib/glib-init.h index 4c812d9d6..b77164c73 100644 --- a/glib/glib-init.h +++ b/glib/glib-init.h @@ -40,7 +40,9 @@ void g_clock_win32_init (void); void g_crash_handler_win32_init (void); void g_crash_handler_win32_deinit (void); gboolean _g_win32_call_rtl_version (OSVERSIONINFOEXW *info); + extern HMODULE glib_dll; +gchar *g_win32_find_helper_executable_path (const gchar *process_name, void *dll_handle); #endif #endif /* __GLIB_INIT_H__ */ diff --git a/glib/glib-private.c b/glib/glib-private.c index 1c0da1947..0a59c6f16 100644 --- a/glib/glib-private.c +++ b/glib/glib-private.c @@ -54,6 +54,7 @@ glib__private__ (void) g_win32_lstat_utf8, g_win32_readlink_utf8, g_win32_fstat, + g_win32_find_helper_executable_path, #endif }; diff --git a/glib/glib-private.h b/glib/glib-private.h index 85987e272..943252f1b 100644 --- a/glib/glib-private.h +++ b/glib/glib-private.h @@ -120,7 +120,6 @@ gboolean g_check_setuid (void); GMainContext * g_main_context_new_with_next_id (guint next_id); #ifdef G_OS_WIN32 -gchar *_glib_get_dll_directory (void); GLIB_AVAILABLE_IN_ALL gchar *_glib_get_locale_dir (void); #endif @@ -168,6 +167,10 @@ typedef struct { int (* g_win32_fstat) (int fd, GWin32PrivateStat *buf); + + /* See gwin32.c */ + gchar *(*g_win32_find_helper_executable_path) (const gchar *process_name, + void *dll_handle); #endif diff --git a/glib/gspawn-win32.c b/glib/gspawn-win32.c index 182928cb4..b43778156 100644 --- a/glib/gspawn-win32.c +++ b/glib/gspawn-win32.c @@ -42,10 +42,11 @@ #include "config.h" -#include "glib.h" +#include "glib-init.h" #include "glib-private.h" -#include "gprintfint.h" +#include "glib.h" #include "glibintl.h" +#include "gprintfint.h" #include "gspawn-private.h" #include "gthread.h" @@ -586,7 +587,6 @@ fork_exec (gint *exit_status, gint conv_error_index; gchar *helper_process; wchar_t *whelper, **wargv, **wenvp; - gchar *glib_dll_directory; int stdin_pipe[2] = { -1, -1 }; int stdout_pipe[2] = { -1, -1 }; int stderr_pipe[2] = { -1, -1 }; @@ -651,16 +651,8 @@ fork_exec (gint *exit_status, helper_process = HELPER_PROCESS "-console.exe"; else helper_process = HELPER_PROCESS ".exe"; - - glib_dll_directory = _glib_get_dll_directory (); - if (glib_dll_directory != NULL) - { - helper_process = g_build_filename (glib_dll_directory, helper_process, NULL); - g_free (glib_dll_directory); - } - else - helper_process = g_strdup (helper_process); + helper_process = g_win32_find_helper_executable_path (helper_process, glib_dll); new_argv[0] = protect_argv_string (helper_process); _g_sprintf (args[ARG_CHILD_ERR_REPORT], "%d", child_err_report_pipe[1]); diff --git a/glib/gutils.c b/glib/gutils.c index 6cc450607..c6aec9e6f 100644 --- a/glib/gutils.c +++ b/glib/gutils.c @@ -104,47 +104,6 @@ #include #endif -#ifdef G_PLATFORM_WIN32 - -gchar * -_glib_get_dll_directory (void) -{ - gchar *retval; - gchar *p; - wchar_t wc_fn[MAX_PATH]; - -#ifdef DLL_EXPORT - if (glib_dll == NULL) - return NULL; -#endif - - /* This code is different from that in - * g_win32_get_package_installation_directory_of_module() in that - * here we return the actual folder where the GLib DLL is. We don't - * do the check for it being in a "bin" or "lib" subfolder and then - * returning the parent of that. - * - * In a statically built GLib, glib_dll will be NULL and we will - * thus look up the application's .exe file's location. - */ - if (!GetModuleFileNameW (glib_dll, wc_fn, MAX_PATH)) - return NULL; - - retval = g_utf16_to_utf8 (wc_fn, -1, NULL, NULL, NULL); - - p = strrchr (retval, G_DIR_SEPARATOR); - if (p == NULL) - { - /* Wtf? */ - return NULL; - } - *p = '\0'; - - return retval; -} - -#endif - /** * g_memmove: * @dest: the destination address to copy the bytes to. diff --git a/glib/gwin32.c b/glib/gwin32.c index e2f73e135..b2b5ff69d 100644 --- a/glib/gwin32.c +++ b/glib/gwin32.c @@ -1336,4 +1336,123 @@ g_crash_handler_win32_deinit (void) WinVEH_handle = NULL; } +/** + * g_win32_find_helper_executable_path: + * @executable_name: (transfer none): name of the helper executable to find + * (something like gspawn-win64-helper.exe or gdbus.exe for example). + * @dll_handle: handle of the DLL to use as searching base path. Pass NULL + * to take current process executable as searching base path. + * + * Find an external executable path and name starting in the same folder + * as a specified DLL or current process executable path. Helper executables + * (like gspawn-win64-helper.exe, gspawn-win64-helper-console.exe or + * gdbus.exe for example) are generally installed in the same folder as the + * corresponding DLL file. + * + * So, if package has been correctly installed, with a dynamic build of GLib, + * the helper executable should be in the same directory as the corresponding + * DLL file and searching should be straightforward. + * + * But if built statically, DLL handle is not available and we have to start + * searching from the directory holding current executable. It may be very + * different from the directory containing the helper program. In order to + * find the right helper program automatically in all common situations, we + * use this pattern: + * + * current directory + * |-- ??? + * |-- bin + * | |-- ??? + * |-- lib + * | |-- ??? + * |-- glib + * | |-- ??? + * |-- gio + * |-- ??? + * + * starting at base searching path (DLL or current executable directory) and + * getting up until the root path. If we cannot still find the helper program, + * we'll rely on PATH as the last resort. + * + * Returns: (transfer full) (type filename) (nullable): the helper executable + * path and name in the GLib filename encoding or NULL in case of error. It + * should be deallocated with g_free(). + */ +gchar * +g_win32_find_helper_executable_path (const gchar *executable_name, void *dll_handle) +{ + static const gchar *const subdirs[] = { "", "bin", "lib", "glib", "gio" }; + static const gsize nb_subdirs = G_N_ELEMENTS (subdirs); + + DWORD module_path_len; + wchar_t module_path[MAX_PATH + 2] = { 0 }; + gchar *base_searching_path; + gchar *p; + gchar *executable_path; + gsize i; + + g_return_val_if_fail (executable_name && *executable_name, NULL); + + module_path_len = GetModuleFileNameW (dll_handle, module_path, MAX_PATH + 1); + /* The > MAX_PATH check prevents truncated module path usage */ + if (module_path_len == 0 || module_path_len > MAX_PATH) + return NULL; + + base_searching_path = g_utf16_to_utf8 (module_path, -1, NULL, NULL, NULL); + if (base_searching_path == NULL) + return NULL; + + p = strrchr (base_searching_path, G_DIR_SEPARATOR); + if (p == NULL) + { + g_free (base_searching_path); + return NULL; + } + *p = '\0'; + + for (;;) + { + /* Search in subdirectories */ + for (i = 0; i < nb_subdirs; ++i) + { + /* As this function is exclusively used on Windows, the + * executable_path is always an absolute path. At worse, when + * reaching the root of the filesystem, base_searching_path may + * equal something like "[Drive letter]:" but never "/" like on + * Linux or Mac. + * For the peace of mind we still assert this, just in case that + * one day someone tries to use this function on Linux or Mac. + */ + executable_path = g_build_filename (base_searching_path, subdirs[i], executable_name, NULL); + g_assert (g_path_is_absolute (executable_path)); + if (g_file_test (executable_path, G_FILE_TEST_IS_REGULAR)) + break; + + g_free (executable_path); + executable_path = NULL; + } + + if (executable_path != NULL) + break; + + /* Let's get one directory level up */ + p = strrchr (base_searching_path, G_DIR_SEPARATOR); + if (p == NULL) + break; + + *p = '\0'; + } + g_free (base_searching_path); + + if (executable_path == NULL) + { + /* Search in system PATH */ + executable_path = g_find_program_in_path (executable_name); + if (executable_path == NULL) + executable_path = g_strdup (executable_name); + } + + return executable_path; +} + #endif From 38abadab3d0b3581a1a6485106cbf1ad6479c363 Mon Sep 17 00:00:00 2001 From: Loic Le Page Date: Mon, 10 Jan 2022 20:44:45 +0100 Subject: [PATCH 2/4] Fix dbus process autolaunch with static build on Windows On Windows, dbus is launched by spawning the gdbus.exe executable on demand from the gio module. When linked dynamically, the executable path is guessed relatively to the gio DLL path. But when linked statically, the only reference path available is the current executable path. In this case, gdbus.exe is not necessarily in the same folder as the current executable. This patch solves the issue using the same algorithm as the one used with process spawning in glib core source code two commits above. --- gio/gdbusprivate.c | 131 +++++++++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 59 deletions(-) diff --git a/gio/gdbusprivate.c b/gio/gdbusprivate.c index 041fab7a8..0b8630ab2 100644 --- a/gio/gdbusprivate.c +++ b/gio/gdbusprivate.c @@ -23,27 +23,28 @@ #include #include -#include "giotypes.h" -#include "gioenumtypes.h" -#include "gsocket.h" #include "gdbusauthobserver.h" -#include "gdbusprivate.h" -#include "gdbusmessage.h" #include "gdbusconnection.h" -#include "gdbusproxy.h" +#include "gdbusdaemon.h" #include "gdbuserror.h" #include "gdbusintrospection.h" -#include "gdbusdaemon.h" -#include "giomodule-priv.h" -#include "gtask.h" +#include "gdbusmessage.h" +#include "gdbusprivate.h" +#include "gdbusproxy.h" #include "ginputstream.h" -#include "gmemoryinputstream.h" +#include "gioenumtypes.h" +#include "giomodule-priv.h" #include "giostream.h" +#include "giotypes.h" +#include "glib-private.h" #include "glib/gstdio.h" +#include "gmemoryinputstream.h" +#include "gsocket.h" #include "gsocketaddress.h" -#include "gsocketcontrolmessage.h" #include "gsocketconnection.h" +#include "gsocketcontrolmessage.h" #include "gsocketoutputstream.h" +#include "gtask.h" #ifdef G_OS_UNIX #include "gunixfdmessage.h" @@ -2275,6 +2276,26 @@ g_win32_run_session_bus (void* hwnd, void* hinst, const char* cmdline, int cmdsh static gboolean autolaunch_binary_absent = FALSE; +static wchar_t * +find_dbus_process_path (void) +{ + wchar_t *dbus_path; + gchar *exe_path = GLIB_PRIVATE_CALL (g_win32_find_helper_executable_path) ("gdbus.exe", _g_io_win32_get_module ()); + dbus_path = g_utf8_to_utf16 (exe_path, -1, NULL, NULL, NULL); + g_free (exe_path); + + if (dbus_path == NULL) + return NULL; + + if (GetFileAttributesW (dbus_path) == INVALID_FILE_ATTRIBUTES) + { + g_free (dbus_path); + return NULL; + } + + return dbus_path; +} + gchar * _g_dbus_win32_get_session_address_dbus_launch (GError **error) { @@ -2292,61 +2313,53 @@ _g_dbus_win32_get_session_address_dbus_launch (GError **error) if (address == NULL && !autolaunch_binary_absent) { - wchar_t gio_path[MAX_PATH + 2] = { 0 }; - int gio_path_len = GetModuleFileNameW (_g_io_win32_get_module (), gio_path, MAX_PATH + 1); + wchar_t *dbus_path = find_dbus_process_path (); + if (dbus_path == NULL) + { + /* warning won't be raised another time + * since autolaunch_binary_absent would be already set. + */ + autolaunch_binary_absent = TRUE; + g_warning ("win32 session dbus binary not found"); + } + else + { + PROCESS_INFORMATION pi = { 0 }; + STARTUPINFOW si = { 0 }; + BOOL res = FALSE; + wchar_t args[MAX_PATH * 2 + 100] = { 0 }; + wchar_t working_dir[MAX_PATH + 2] = { 0 }; + wchar_t *p; - /* The <= MAX_PATH check prevents truncated path usage */ - if (gio_path_len > 0 && gio_path_len <= MAX_PATH) - { - PROCESS_INFORMATION pi = { 0 }; - STARTUPINFOW si = { 0 }; - BOOL res = FALSE; - wchar_t exe_path[MAX_PATH + 100] = { 0 }; - /* calculate index of first char of dll file name inside full path */ - int gio_name_index = gio_path_len; - for (; gio_name_index > 0; --gio_name_index) - { - wchar_t prev_char = gio_path[gio_name_index - 1]; - if (prev_char == L'\\' || prev_char == L'/') - break; - } - gio_path[gio_name_index] = L'\0'; - wcscpy (exe_path, gio_path); - wcscat (exe_path, L"\\gdbus.exe"); + wcscpy (working_dir, dbus_path); + p = wcsrchr (working_dir, L'\\'); + if (p != NULL) + *p = L'\0'; - if (GetFileAttributesW (exe_path) == INVALID_FILE_ATTRIBUTES) - { - /* warning won't be raised another time - * since autolaunch_binary_absent would be already set. - */ - autolaunch_binary_absent = TRUE; - g_warning ("win32 session dbus binary not found: %S", exe_path ); - } - else - { - wchar_t args[MAX_PATH*2 + 100] = { 0 }; - wcscpy (args, L"\""); - wcscat (args, exe_path); - wcscat (args, L"\" "); + wcscpy (args, L"\""); + wcscat (args, dbus_path); + wcscat (args, L"\" "); #define _L_PREFIX_FOR_EXPANDED(arg) L##arg #define _L_PREFIX(arg) _L_PREFIX_FOR_EXPANDED (arg) - wcscat (args, _L_PREFIX (_GDBUS_ARG_WIN32_RUN_SESSION_BUS)); + wcscat (args, _L_PREFIX (_GDBUS_ARG_WIN32_RUN_SESSION_BUS)); #undef _L_PREFIX #undef _L_PREFIX_FOR_EXPANDED - res = CreateProcessW (exe_path, args, - 0, 0, FALSE, - NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS, - 0, gio_path, - &si, &pi); - } - if (res) - { - address = read_shm (DBUS_DAEMON_ADDRESS_INFO); - if (address == NULL) - g_warning ("%S dbus binary failed to launch bus, maybe incompatible version", exe_path ); - } - } + res = CreateProcessW (dbus_path, args, + 0, 0, FALSE, + NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW | DETACHED_PROCESS, + 0, working_dir, + &si, &pi); + + if (res) + { + address = read_shm (DBUS_DAEMON_ADDRESS_INFO); + if (address == NULL) + g_warning ("%S dbus binary failed to launch bus, maybe incompatible version", dbus_path); + } + + g_free (dbus_path); + } } release_mutex (autolaunch_mutex); From 4f6673fb586c950d82ec362f0d914ae4105ce254 Mon Sep 17 00:00:00 2001 From: Loic Le Page Date: Wed, 12 Jan 2022 10:19:34 +0100 Subject: [PATCH 3/4] Fix glib/tests/completion test GLists compare pointers values and not strings content. --- glib/tests/completion.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/glib/tests/completion.c b/glib/tests/completion.c index 223b3df52..4f790373b 100644 --- a/glib/tests/completion.c +++ b/glib/tests/completion.c @@ -34,6 +34,11 @@ static void test_completion (void) { + static const char *const a1 = "a\302\243"; + static const char *const a2 = "a\302\244"; + static const char *const bb = "bb"; + static const char *const bc = "bc"; + GCompletion *cmp; GList *items; gchar *prefix; @@ -42,10 +47,10 @@ test_completion (void) g_completion_set_compare (cmp, strncmp); items = NULL; - items = g_list_append (items, "a\302\243"); - items = g_list_append (items, "a\302\244"); - items = g_list_append (items, "bb"); - items = g_list_append (items, "bc"); + items = g_list_append (items, (gpointer) a1); + items = g_list_append (items, (gpointer) a2); + items = g_list_append (items, (gpointer) bb); + items = g_list_append (items, (gpointer) bc); g_completion_add_items (cmp, items); g_list_free (items); @@ -75,7 +80,7 @@ test_completion (void) items = g_completion_complete_utf8 (cmp, "a", NULL); g_assert_cmpint (g_list_length (items), ==, 2); - items = g_list_append (NULL, "bb"); + items = g_list_append (NULL, (gpointer) bb); g_completion_remove_items (cmp, items); g_list_free (items); From cbbc9206aa575021f91aa8721691080c1a2aaf69 Mon Sep 17 00:00:00 2001 From: Loic Le Page Date: Sat, 22 Jan 2022 10:45:45 +0100 Subject: [PATCH 4/4] Fix exported symbols in proxy-libintl subproject --- subprojects/proxy-libintl.wrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/subprojects/proxy-libintl.wrap b/subprojects/proxy-libintl.wrap index 3523a5041..0e6c8529c 100644 --- a/subprojects/proxy-libintl.wrap +++ b/subprojects/proxy-libintl.wrap @@ -1,5 +1,5 @@ [wrap-git] directory=proxy-libintl url=https://github.com/frida/proxy-libintl.git -revision=0.2 +revision=c03e1a74b17fa7ec467e110130775409e4828a4c depth=1