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'))
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)
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)
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)