Пример #1
0
    def test_glibc_mangling(self):
        dynamic_linker = elf.find_linker(
            root_path=self.path, snap_base_path='/snap/snap-name/current')

        self.get_packages_mock.assert_called_once_with('libc6')

        self.assertThat(dynamic_linker, EndsWith('ld-2.26.so'))
Пример #2
0
    def patch(self) -> None:
        """Executes the patching process for elf_files.

        First it verifies there are no GLIBC mismatches, if any are found,
        libc6 must be part of stage-packages. The verification is only
        performed if the core base is not compatible with the host the
        snapcraft build is taking place on (e.g.; building on 18.04 while
        targeting 'core' as the core base).

        If the snapcraft project is not a classic confined one and there
        are no GLIBC mismatches, then this method returns with no modifications
        to elf_files. However, if one of those cases are true, the
        required dynamic linker is retrieved and the necessary elf files
        in elf_files are patched to behave accordinginly in the environment by
        means of setting the correct interpreter and rpaths (not runpaths).

        :raises errors.SnapcraftMissingLinkerInBaseError:
            if the linker within the core base cannot be found.
        :raises errors.StagePackageMissingError:
            if there are libc6 mismatches and libc6 is not in stage-packages.
        :raises errors.SnapcraftEnvironementError:
            if something is horribly wrong.
        """
        # Just return if this is a static base and libc6 has not been staged.
        if self._project.is_static_base(self._core_base) and not self._is_libc6_staged:
            return
        if not (
            self._project.is_static_base(self._core_base)
            or self._project.is_host_compatible_with_base(self._core_base)
        ):
            logger.debug("Host is not compatible with base")
            self._verify_compat()
        logger.debug("Is classic: {!r}".format(self._is_classic))
        logger.debug("Is libc6 in stage-packages: {!r}".format(self._is_libc6_staged))
        if not (self._is_classic or self._is_libc6_staged):
            return

        if self._is_libc6_staged:
            dynamic_linker = elf.find_linker(
                root_path=self._primedir, snap_base_path=self._snap_base_path
            )
            logger.warning(
                "libc6 has been staged into the snap: only do this if you know what "
                "what you are doing."
            )
        elif self._is_classic:
            dynamic_linker = self._project.get_core_dynamic_linker(
                self._core_base, expand=False
            )
        else:
            raise errors.SnapcraftEnvironmentError(
                "An unexpected error has occurred while patching. "
                "Please log an issue against the snapcraft tool."
            )

        logger.debug("Dynamic linker set to {!r}".format(dynamic_linker))
        self._patch(dynamic_linker)
Пример #3
0
    def patch(self) -> None:
        """Executes the patching process for elf_files.

        First it verifies there are no GLIBC mismatches, if any are found,
        libc6 must be part of stage-packages. The verification is only
        performed if the core base is not compatible with the host the
        snapcraft build is taking place on (e.g.; building on 18.04 while
        targeting 'core' as the core base).

        If the snapcraft project is not a classic confined one and there
        are no GLIBC mismatches, then this method returns with no modifications
        to elf_files. However, if one of those cases are true, the
        required dynamic linker is retrieved and the necessary elf files
        in elf_files are patched to behave accordinginly in the environment by
        means of setting the correct interpreter and rpaths (not runpaths).

        :raises errors.SnapcraftMissingLinkerInBaseError:
            if the linker within the core base cannot be found.
        :raises errors.StagePackageMissingError:
            if there are libc6 mismatches and libc6 is not in stage-packages.
        :raises errors.SnapcraftEnvironementError:
            if something is horribly wrong.
        """
        logger.debug(
            "Host compatible with base: {!r}".format(self._is_host_compat_with_base)
        )
        if not self._is_host_compat_with_base:
            self._verify_compat()
        logger.debug("Is classic: {!r}".format(self._is_classic))
        logger.debug("Is libc6 in stage-packages: {!r}".format(self._is_libc6_staged))
        if not (self._is_classic or self._is_libc6_staged):
            return

        if self._is_libc6_staged:
            dynamic_linker = elf.find_linker(
                root_path=self._primedir, snap_base_path=self._snap_base_path
            )
        elif self._is_classic:
            dynamic_linker = self._project.get_core_dynamic_linker(
                self._core_base, expand=False
            )
        else:
            raise errors.SnapcraftEnvironmentError(
                "An unexpected error has occurred while patching. "
                "Please log an issue against the snapcraft tool."
            )

        logger.debug("Dynamic linker set to {!r}".format(dynamic_linker))
        self._patch(dynamic_linker)
Пример #4
0
    def prime(self, force=False) -> None:  # noqa: C901
        self.makedirs()
        self.notify_part_progress('Priming')
        snap_files, snap_dirs = self.migratable_fileset_for('prime')
        _migrate_files(snap_files, snap_dirs, self.stagedir, self.primedir)

        elf_files = elf.get_elf_files(self.primedir, snap_files)
        all_dependencies = set()
        # TODO: base snap support
        core_path = common.get_core_path()

        # Clear the cache of all libs that aren't already in the primedir
        self._soname_cache.reset_except_root(self.primedir)
        for elf_file in elf_files:
            all_dependencies.update(
                elf_file.load_dependencies(root_path=self.primedir,
                                           core_base_path=core_path,
                                           soname_cache=self._soname_cache))

        dependency_paths = self._handle_dependencies(all_dependencies)

        if not self._build_attributes.keep_execstack():
            clear_execstack(elf_files=elf_files)

        # TODO revisit if we need to support variations and permutations
        #  of this
        staged_patchelf_path = os.path.join(self.stagedir, 'bin', 'patchelf')
        if not os.path.exists(staged_patchelf_path):
            staged_patchelf_path = None
        # We need to verify now that the GLIBC version would be compatible
        # with that of the base.
        # TODO the linker version depends on the chosen base, but that
        # base may not be installed so we cannot depend on
        # get_core_dynamic_linker to resolve the final path for which
        # we resort to our only working base 16, ld-2.23.so.
        linker_incompat = dict()  # type: Dict[str, str]
        for elf_file in elf_files:
            if not elf_file.is_linker_compatible(linker='ld-2.23.so'):
                linker_incompat[elf_file.path] = elf_file.get_required_glibc()
        # If libc6 is staged, to avoid symbol mixups we will resort to
        # glibc mangling.
        libc6_staged = 'libc6' in self._part_properties.get(
            'stage-packages', [])
        is_classic = self._confinement == 'classic'
        # classic confined snaps built on anything but a host supporting the
        # the target base will require glibc mangling.
        classic_mangling_needed = (
            is_classic
            and not self._project_options.is_host_compatible_with_base)
        if linker_incompat:
            formatted_items = [
                '- {} (requires GLIBC {})'.format(k, v)
                for k, v in linker_incompat.items()
            ]
            logger.warning(
                'The GLIBC version of the targeted core is 2.23. A newer '
                'libc will be required for the following files:\n{}'.format(
                    '\n'.join(formatted_items)))

        dynamic_linker = None
        if linker_incompat or libc6_staged or classic_mangling_needed:
            if not libc6_staged:
                raise errors.StagePackageMissingError(package='libc6')
            dynamic_linker = elf.find_linker(
                root_path=self.primedir, snap_base_path=self._snap_base_path)
        elif is_classic:
            dynamic_linker = self._project_options.get_core_dynamic_linker()

        if dynamic_linker:
            elf_patcher = elf.Patcher(
                dynamic_linker=dynamic_linker,
                root_path=self.primedir,
                preferred_patchelf_path=staged_patchelf_path)
            for elf_file in elf_files:
                elf_patcher.patch(elf_file=elf_file)

        self.mark_prime_done(snap_files, snap_dirs, dependency_paths)