diff --git a/pressure-vessel/elf-utils.c b/pressure-vessel/elf-utils.c deleted file mode 100644 index 466003e5807f0b300f0d72eb5c63b718895f5c16..0000000000000000000000000000000000000000 --- a/pressure-vessel/elf-utils.c +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright © 2017-2020 Collabora Ltd. - * - * SPDX-License-Identifier: LGPL-2.1-or-later - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#include "elf-utils.h" - -#include <glib.h> -#include <glib/gstdio.h> -#include <gio/gio.h> -#include <libelf.h> - -#include "libglnx/libglnx.h" - -#include "steam-runtime-tools/glib-backports-internal.h" - -#define throw_elf_error(error, format, ...) \ - glnx_null_throw (error, format ": %s", ##__VA_ARGS__, elf_errmsg (elf_errno ())) - -/* - * pv_elf_open_fd: - * @fd: An open file descriptor - * - * Returns: (transfer full): A libelf object representing the library - */ -Elf * -pv_elf_open_fd (int fd, - GError **error) -{ - g_autoptr(Elf) elf = NULL; - - g_return_val_if_fail (fd >= 0, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (elf_version (EV_CURRENT) == EV_NONE) - return throw_elf_error (error, "elf_version(EV_CURRENT)"); - - elf = elf_begin (fd, ELF_C_READ, NULL); - - if (elf == NULL) - return throw_elf_error (error, "elf_begin"); - - return g_steal_pointer (&elf); -} - -/* - * pv_elf_get_soname: - * @elf: A libelf object - * - * Return the `DT_SONAME` header, or %NULL on error - */ -gchar * -pv_elf_get_soname (Elf *elf, - GError **error) -{ - GElf_Ehdr ehdr; - size_t phdr_count = 0; - size_t i; - GElf_Phdr phdr_mem; - GElf_Phdr *phdr = NULL; - GElf_Dyn *dynamic_header = NULL; - GElf_Dyn dynamic_header_mem; - Elf_Scn *dynamic_section = NULL; - GElf_Shdr dynamic_section_header_mem; - GElf_Shdr *dynamic_section_header = NULL; - Elf_Scn *string_table_section = NULL; - GElf_Shdr string_table_header_mem; - GElf_Shdr *string_table_header = NULL; - Elf_Data *data = NULL; - size_t size_per_dyn; - const char *soname = NULL; - - g_return_val_if_fail (elf != NULL, NULL); - g_return_val_if_fail (error == NULL || *error == NULL, NULL); - - if (elf_kind (elf) != ELF_K_ELF) - return glnx_null_throw (error, "elf_kind %d, expected ELF_K_ELF=%d", - elf_kind (elf), ELF_K_ELF); - - if (gelf_getehdr (elf, &ehdr) == NULL) - return throw_elf_error (error, "elf_getehdr"); - - if (ehdr.e_type != ET_DYN) - return glnx_null_throw (error, "ehdr.e_type %d, expected ET_DYN=%d", - ehdr.e_type, ET_DYN); - - if (elf_getphdrnum (elf, &phdr_count) != 0) - return throw_elf_error (error, "elf_getphdrnum"); - - for (i = 0; i < phdr_count; i++) - { - phdr = gelf_getphdr (elf, i, &phdr_mem); - - if (phdr != NULL && phdr->p_type == PT_DYNAMIC) - { - dynamic_section = gelf_offscn (elf, phdr->p_offset); - dynamic_section_header = gelf_getshdr (dynamic_section, - &dynamic_section_header_mem); - break; - } - } - - if (dynamic_section == NULL || dynamic_section_header == NULL) - return glnx_null_throw (error, "Unable to find dynamic section header"); - - string_table_section = elf_getscn (elf, dynamic_section_header->sh_link); - string_table_header = gelf_getshdr (string_table_section, - &string_table_header_mem); - - if (string_table_section == NULL || string_table_header == NULL) - return glnx_null_throw (error, "Unable to find linked string table"); - - data = elf_getdata (dynamic_section, NULL); - - if (data == NULL) - return throw_elf_error (error, "elf_getdata(dynamic_section)"); - - size_per_dyn = gelf_fsize (elf, ELF_T_DYN, 1, EV_CURRENT); - - for (i = 0; i < dynamic_section_header->sh_size / size_per_dyn; i++) - { - dynamic_header = gelf_getdyn (data, i, &dynamic_header_mem); - - if (dynamic_header == NULL) - break; - - if (dynamic_header->d_tag == DT_SONAME) - { - soname = elf_strptr (elf, dynamic_section_header->sh_link, - dynamic_header->d_un.d_val); - - if (soname == NULL) - return glnx_null_throw (error, "Unable to read DT_SONAME"); - - - } - } - - if (soname == NULL) - return glnx_null_throw (error, "Unable to find DT_SONAME"); - - return g_strdup (soname); -} - diff --git a/pressure-vessel/elf-utils.h b/pressure-vessel/elf-utils.h deleted file mode 100644 index cb0529cf8c6cb50c62d2bffc403496cbf9f3b998..0000000000000000000000000000000000000000 --- a/pressure-vessel/elf-utils.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright © 2020 Collabora Ltd. - * - * SPDX-License-Identifier: LGPL-2.1-or-later - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, see <http://www.gnu.org/licenses/>. - */ - -#pragma once - -#include <gelf.h> -#include <glib.h> -#include <libelf.h> - -#include "libglnx/libglnx.h" - -#include "steam-runtime-tools/glib-backports-internal.h" - -G_DEFINE_AUTOPTR_CLEANUP_FUNC (Elf, elf_end); - -Elf *pv_elf_open_fd (int fd, - GError **error); - -gchar *pv_elf_get_soname (Elf *elf, - GError **error); diff --git a/pressure-vessel/meson.build b/pressure-vessel/meson.build index 757e68af49eb98471d9eaf1926a73c2114eb6cd1..d1c8eb37c69714e9cae8c00066a1ab9d0471d503 100644 --- a/pressure-vessel/meson.build +++ b/pressure-vessel/meson.build @@ -77,8 +77,6 @@ pressure_vessel_utils = static_library( sources : [ 'bwrap-lock.c', 'bwrap-lock.h', - 'elf-utils.c', - 'elf-utils.h', 'flatpak-bwrap.c', 'flatpak-bwrap-private.h', 'flatpak-utils-base.c', @@ -94,7 +92,6 @@ pressure_vessel_utils = static_library( dependencies : [ threads, gio_unix, - libelf, libglnx_dep, libsteamrt_static_dep, ], diff --git a/pressure-vessel/runtime.c b/pressure-vessel/runtime.c index 645955af74b4c9b06663638a8793dc2aaee45753..8fd008ff4ad2a8a447ca11e4d52e29363522bcf7 100644 --- a/pressure-vessel/runtime.c +++ b/pressure-vessel/runtime.c @@ -39,7 +39,6 @@ #include "bwrap.h" #include "bwrap-lock.h" -#include "elf-utils.h" #include "enumtypes.h" #include "exports.h" #include "flatpak-run-private.h" @@ -2837,18 +2836,17 @@ pv_runtime_remove_overridden_libraries (PvRuntime *self, while (TRUE) { - g_autoptr(Elf) elf = NULL; - g_autoptr(GError) local_error = NULL; - glnx_autofd int libfd = -1; g_autofree gchar *path = NULL; - g_autofree gchar *soname = NULL; g_autofree gchar *target = NULL; + const char *target_base; if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&iters[i], &dent, NULL, error)) - return glnx_prefix_error (error, "Unable to iterate over \"%s/%s\"", - self->mutable_sysroot, - libdir); + { + glnx_prefix_error (error, "Unable to iterate over \"%s/%s\"", + self->mutable_sysroot, libdir); + goto out; + } if (dent == NULL) break; @@ -2877,18 +2875,38 @@ pv_runtime_remove_overridden_libraries (PvRuntime *self, continue; path = g_build_filename (libdir, dent->d_name, NULL); + target = glnx_readlinkat_malloc (iters[i].fd, dent->d_name, + NULL, NULL); + if (target != NULL) + target_base = glnx_basename (target); + else + target_base = NULL; + + /* Suppose we have a shared library libcurl.so.4 -> libcurl.so.4.2.0 + * in the container and libcurl.so.4.7.0 in the provider, + * with a backwards-compatibility alias libcurl.so.3. + * dent->d_name might be any of those strings. */ /* scope for soname_link */ + if (TRUE) /* to avoid -Wmisleading-indentation */ { g_autofree gchar *soname_link = NULL; + /* If we're looking at + * /usr/lib/MULTIARCH/libcurl.so.4 -> libcurl.so.4.2.0, and a + * symlink .../overrides/lib/MULTIARCH/libcurl.so.4 exists, then + * we want to delete /usr/lib/MULTIARCH/libcurl.so.4 and + * /usr/lib/MULTIARCH/libcurl.so.4.2.0. */ soname_link = g_build_filename (arch->libdir_in_current_namespace, dent->d_name, NULL); - /* If we found libfoo.so.1 in the container, and libfoo.so.1 - * also exists among the overrides, delete it. */ if (g_file_test (soname_link, G_FILE_TEST_IS_SYMLINK)) { + if (target_base != NULL) + g_hash_table_replace (delete[i], + g_strdup (target_base), + g_strdup (soname_link)); + g_hash_table_replace (delete[i], g_strdup (dent->d_name), g_steal_pointer (&soname_link)); @@ -2896,22 +2914,63 @@ pv_runtime_remove_overridden_libraries (PvRuntime *self, } } - target = glnx_readlinkat_malloc (iters[i].fd, dent->d_name, - NULL, NULL); + /* scope for alias_link */ + if (TRUE) /* to avoid -Wmisleading-indentation */ + { + g_autofree gchar *alias_link = NULL; + g_autofree gchar *alias_target = NULL; + + /* If we're looking at + * /usr/lib/MULTIARCH/libcurl.so.3 -> libcurl.so.4, and a + * symlink .../aliases/libcurl.so.3 exists and points to + * e.g. .../overrides/lib/$MULTIARCH/libcurl.so.4, then + * /usr/lib/MULTIARCH/libcurl.so.3 was overridden and should + * be deleted; /usr/lib/MULTIARCH/libcurl.so.4 should also + * be deleted. + * + * However, if .../aliases/libcurl.so.3 points to + * e.g. /usr/lib/MULTIARCH/libcurl.so.4, then the container's + * library was not overridden and we should not delete + * anything. */ + alias_link = g_build_filename (arch->aliases_in_current_namespace, + dent->d_name, NULL); + alias_target = glnx_readlinkat_malloc (AT_FDCWD, alias_link, + NULL, NULL); + + if (alias_target != NULL + && flatpak_has_path_prefix (alias_target, + self->overrides_in_container)) + { + if (target_base != NULL) + g_hash_table_replace (delete[i], + g_strdup (target_base), + g_strdup (alias_link)); + + g_hash_table_replace (delete[i], + g_strdup (dent->d_name), + g_steal_pointer (&alias_link)); + continue; + } + } if (target != NULL) { g_autofree gchar *soname_link = NULL; + /* If we're looking at + * /usr/lib/MULTIARCH/libcurl.so -> libcurl.so.4, and a + * symlink .../overrides/lib/MULTIARCH/libcurl.so.4 exists, + * then we want to delete /usr/lib/MULTIARCH/libcurl.so + * and /usr/lib/MULTIARCH/libcurl.so.4. */ soname_link = g_build_filename (arch->libdir_in_current_namespace, glnx_basename (target), NULL); - /* If the symlink in the container points to - * /foo/bar/libfoo.so.1, and libfoo.so.1 also exists among - * the overrides, delete it. */ if (g_file_test (soname_link, G_FILE_TEST_IS_SYMLINK)) { + g_hash_table_replace (delete[i], + g_strdup (target_base), + g_strdup (soname_link)); g_hash_table_replace (delete[i], g_strdup (dent->d_name), g_steal_pointer (&soname_link)); @@ -2919,48 +2978,78 @@ pv_runtime_remove_overridden_libraries (PvRuntime *self, } } - libfd = _srt_resolve_in_sysroot (self->mutable_sysroot_fd, path, - SRT_RESOLVE_FLAGS_READABLE, NULL, - &local_error); - - if (libfd < 0) + if (target != NULL) { - g_warning ("Unable to open %s/%s for reading: %s", - self->mutable_sysroot, path, local_error->message); - g_clear_error (&local_error); - continue; + g_autofree gchar *alias_link = NULL; + g_autofree gchar *alias_target = NULL; + + /* If we're looking at + * /usr/lib/MULTIARCH/libcurl.so.3 -> libcurl.so.4, and a + * symlink .../aliases/libcurl.so.3 exists and points to + * e.g. .../overrides/lib/$MULTIARCH/libcurl.so.4, then + * /usr/lib/MULTIARCH/libcurl.so.3 was overridden and should + * be deleted; /usr/lib/MULTIARCH/libcurl.so.4 should also + * be deleted. + * + * However, if .../aliases/libcurl.so.3 points to + * e.g. /usr/lib/MULTIARCH/libcurl.so.4, then the container's + * library was not overridden and we should not delete it. */ + alias_link = g_build_filename (arch->aliases_in_current_namespace, + glnx_basename (target), + NULL); + alias_target = glnx_readlinkat_malloc (AT_FDCWD, alias_link, + NULL, NULL); + + if (alias_target != NULL + && flatpak_has_path_prefix (alias_target, + self->overrides_in_container)) + { + g_hash_table_replace (delete[i], + g_strdup (target_base), + g_strdup (alias_link)); + g_hash_table_replace (delete[i], + g_strdup (dent->d_name), + g_steal_pointer (&alias_link)); + continue; + } } + } - elf = pv_elf_open_fd (libfd, &local_error); + /* Iterate over the directory again, to clean up dangling development + * symlinks */ + glnx_dirfd_iterator_rewind (&iters[i]); - if (elf != NULL) - soname = pv_elf_get_soname (elf, &local_error); + while (TRUE) + { + g_autofree gchar *target = NULL; + gpointer reason; - if (soname == NULL) + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&iters[i], &dent, + NULL, error)) { - g_warning ("Unable to get SONAME of %s/%s: %s", - self->mutable_sysroot, path, local_error->message); - g_clear_error (&local_error); - continue; + glnx_prefix_error (error, "Unable to iterate over \"%s/%s\"", + self->mutable_sysroot, libdir); + goto out; } - /* If we found a library with SONAME libfoo.so.1 in the - * container, and libfoo.so.1 also exists among the overrides, - * delete it. */ - { - g_autofree gchar *soname_link = NULL; + if (dent == NULL) + break; - soname_link = g_build_filename (arch->libdir_in_current_namespace, - soname, NULL); + if (dent->d_type != DT_LNK) + continue; - if (g_file_test (soname_link, G_FILE_TEST_IS_SYMLINK)) - { - g_hash_table_replace (delete[i], - g_strdup (dent->d_name), - g_steal_pointer (&soname_link)); - continue; - } - } + /* If we were going to delete it anyway, ignore */ + if (g_hash_table_lookup_extended (delete[i], dent->d_name, NULL, NULL)) + continue; + + target = glnx_readlinkat_malloc (iters[i].fd, dent->d_name, + NULL, NULL); + + /* If we're going to delete the target, also delete the symlink + * rather than leaving it dangling */ + if (g_hash_table_lookup_extended (delete[i], target, NULL, &reason)) + g_hash_table_replace (delete[i], g_strdup (dent->d_name), + g_strdup (reason)); } } diff --git a/subprojects/libglnx/glnx-dirfd.c b/subprojects/libglnx/glnx-dirfd.c index 6d1e2d216f0f7bbadbdef778432572813898ba50..0a63bcbc8016d48437558c24337eef9952779280 100644 --- a/subprojects/libglnx/glnx-dirfd.c +++ b/subprojects/libglnx/glnx-dirfd.c @@ -173,6 +173,24 @@ glnx_dirfd_iterator_next_dent (GLnxDirFdIterator *dfd_iter, return TRUE; } +/** + * glnx_dirfd_iterator_rewind: + * @dfd_iter: A directory iterator + * + * Rewind to the beginning of @dfd_iter. The next call to + * glnx_dirfd_iterator_next_dent() will provide the first entry in the + * directory. + */ +void +glnx_dirfd_iterator_rewind (GLnxDirFdIterator *dfd_iter) +{ + GLnxRealDirfdIterator *real_dfd_iter = (GLnxRealDirfdIterator*) dfd_iter; + + g_return_if_fail (dfd_iter->initialized); + + rewinddir (real_dfd_iter->d); +} + /** * glnx_dirfd_iterator_next_dent_ensure_dtype: * @dfd_iter: A directory iterator diff --git a/subprojects/libglnx/glnx-dirfd.h b/subprojects/libglnx/glnx-dirfd.h index 0046ac8eb651ebe9b753d604d1150a2ab2334478..d5773e729c2e3b3166c4a4b38e2039c305eece48 100644 --- a/subprojects/libglnx/glnx-dirfd.h +++ b/subprojects/libglnx/glnx-dirfd.h @@ -66,6 +66,7 @@ gboolean glnx_dirfd_iterator_next_dent_ensure_dtype (GLnxDirFdIterator *dfd_ite struct dirent **out_dent, GCancellable *cancellable, GError **error); +void glnx_dirfd_iterator_rewind (GLnxDirFdIterator *dfd_iter); void glnx_dirfd_iterator_clear (GLnxDirFdIterator *dfd_iter); G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(GLnxDirFdIterator, glnx_dirfd_iterator_clear) diff --git a/tests/pressure-vessel/meson.build b/tests/pressure-vessel/meson.build index 68f377973ff854ca537195e8b6ca16336a56004f..792fd8907727f454a05fb37fd85ae17e85cd948f 100644 --- a/tests/pressure-vessel/meson.build +++ b/tests/pressure-vessel/meson.build @@ -145,7 +145,6 @@ endforeach # Helper programs and manual tests helpers = [ 'cheap-copy', - 'elf-get-soname', 'helper', ]