From 9bc89b821cf09195418f20225bbec39c0567d263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20=C3=85dahl?= Date: Wed, 17 Aug 2022 21:43:21 +0200 Subject: [PATCH] main: Prepend RPATH or RUNPATH paths to gir search paths If one wants to run tests the non-installed gnome-shell, that currently fails as gnome-shell the executable attempts to link against ./build/src/libgnome-shell.so, but when GObject introspection tries to find what library to link to for Shell, it goes to the installed libgnome-shell.so, causing two different versions of libgnome-shell.so to be loaded. This, however, can be avoided thanks to meson adding $ORIGIN paths to relevant libraries before installing an executable. What this means in practice is that we can inspect ourself upon startup, discover whether the RPATH/RUNPATH header contains $ORIGIN, and if so, expand it to the directory containing the executable, and prepend the introspection search paths with said directory. This effectively means that the introspection machinery now finds the same library that the linker linked the gnome-shell executable with, making it run successfully. It's not possible to use $GI_TYPELIB_PATH since g_irepository_prepend_library_path() takes precedence. There is no "append" variant of that API. Part-of: --- config.h.meson | 3 ++ meson.build | 2 ++ src/main.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) diff --git a/config.h.meson b/config.h.meson index ff355d306..928a758ae 100644 --- a/config.h.meson +++ b/config.h.meson @@ -36,3 +36,6 @@ /* Define if polkit defines autocleanup functions */ #mesondefine HAVE_POLKIT_AUTOCLEANUP + +/* Define to 1 if the and header file exist. */ +#mesondefine HAVE_EXE_INTROSPECTION diff --git a/meson.build b/meson.build index 252fb1ec4..0be7bf769 100644 --- a/meson.build +++ b/meson.build @@ -148,6 +148,8 @@ cdata.set('HAVE_FDWALK', cc.has_function('fdwalk')) cdata.set('HAVE_MALLINFO', cc.has_function('mallinfo')) cdata.set('HAVE_MALLINFO2', cc.has_function('mallinfo2')) cdata.set('HAVE_SYS_RESOURCE_H', cc.has_header('sys/resource.h')) +cdata.set('HAVE_EXE_INTROSPECTION', + cc.has_header('elf.h') and cc.has_header('link.h')) cdata.set('HAVE__NL_TIME_FIRST_WEEKDAY', cc.has_header_symbol('langinfo.h', '_NL_TIME_FIRST_WEEKDAY') ) diff --git a/src/main.c b/src/main.c index 269fe56c9..9913bbccb 100644 --- a/src/main.c +++ b/src/main.c @@ -18,6 +18,11 @@ #include #include #include +#include + +#ifdef HAVE_EXE_INTROSPECTION +#include +#endif #include "shell-global.h" #include "shell-global-private.h" @@ -123,6 +128,74 @@ shell_dbus_init (gboolean replace) g_object_unref (session); } +#ifdef HAVE_EXE_INTROSPECTION +static void +maybe_add_rpath_introspection_paths (void) +{ + ElfW (Dyn) *dyn; + ElfW (Dyn) *rpath = NULL; + ElfW (Dyn) *runpath = NULL; + const char *strtab = NULL; + g_auto (GStrv) paths = NULL; + g_autofree char *exe_dir = NULL; + GStrv str; + + for (dyn = _DYNAMIC; dyn->d_tag != DT_NULL; dyn++) + { + if (dyn->d_tag == DT_RPATH) + rpath = dyn; + else if (dyn->d_tag == DT_RUNPATH) + runpath = dyn; + else if (dyn->d_tag == DT_STRTAB) + strtab = (const char *) dyn->d_un.d_val; + } + + if ((!rpath && !runpath) || !strtab) + return; + + if (rpath) + paths = g_strsplit (strtab + rpath->d_un.d_val, ":", -1); + else + paths = g_strsplit (strtab + runpath->d_un.d_val, ":", -1); + + if (!paths) + return; + + for (str = paths; *str; str++) + { + g_autoptr (GError) error = NULL; + g_autoptr (GString) rpath_dir = NULL; + + if (!strstr (*str, "$ORIGIN")) + continue; + + if (!exe_dir) + { + g_autofree char *exe_path = NULL; + + exe_path = g_file_read_link ("/proc/self/exe", &error); + if (!exe_path) + { + g_warning ("Failed to find directory of executable: %s", + error->message); + return; + } + + exe_dir = g_path_get_dirname (exe_path); + } + + rpath_dir = g_string_new (*str); + g_string_replace (rpath_dir, "$ORIGIN", exe_dir, 0); + + g_debug ("Prepending RPATH directory '%s' " + "to introsepciton library search path", + rpath_dir->str); + g_irepository_prepend_search_path (rpath_dir->str); + g_irepository_prepend_library_path (rpath_dir->str); + } +} +#endif /* HAVE_EXE_INTROSPECTION */ + static void shell_introspection_init (void) { @@ -137,6 +210,10 @@ shell_introspection_init (void) */ g_irepository_prepend_library_path (MUTTER_TYPELIB_DIR); g_irepository_prepend_library_path (GNOME_SHELL_PKGLIBDIR); + +#ifdef HAVE_EXE_INTROSPECTION + maybe_add_rpath_introspection_paths (); +#endif } static void