def test_nothing_to_patch(self): mangling.handle_glibc_mismatch( # Pass part of the slice without fake_elf-2.26 elf_files=self.fake_elf.elf_files[1:], root_path=self.path, core_base_path='/snap/core/current', snap_base_path='/snap/snap-name/current') self.get_packages_mock.assert_not_called() self.assertThat(os.path.join(self.path, 'snap', 'libc6', 'ld-2.26.so'), Not(FileExists())) self.patch_mock.assert_not_called()
def test_glibc_mangling(self): mangling.handle_glibc_mismatch( elf_files=self.fake_elf.elf_files, root_path=self.path, core_base_path='/snap/core/current', snap_base_path='/snap/snap-name/current') self.get_packages_mock.assert_called_once_with('libc6') self.assertThat(os.path.join(self.path, 'snap', 'libc6', 'ld-2.26.so'), FileExists()) # Only fake_elf1 requires a newer libc6 self.patch_mock.assert_called_once_with( elf_file=self.fake_elf.elf_files[0])
def test_nothing_to_patch(self): elf_files = [ self.fake_elf['fake_elf-2.23'], self.fake_elf['fake_elf-1.1'], ] mangling.handle_glibc_mismatch( elf_files=elf_files, root_path=self.path, core_base_path='/snap/core/current', snap_base_path='/snap/snap-name/current') self.get_packages_mock.assert_not_called() self.assertThat(os.path.join(self.path, 'snap', 'libc6', 'ld-2.26.so'), Not(FileExists())) self.patch_mock.assert_not_called()
def test_glibc_mangling(self): elf_files = [ self.fake_elf['fake_elf-2.26'], self.fake_elf['fake_elf-2.23'], self.fake_elf['fake_elf-1.1'], ] mangling.handle_glibc_mismatch( elf_files=elf_files, root_path=self.path, core_base_path='/snap/core/current', snap_base_path='/snap/snap-name/current') self.get_packages_mock.assert_called_once_with('libc6') # Only fake_elf1 requires a newer libc6 self.patch_mock.assert_called_once_with( elf_file=self.fake_elf['fake_elf-2.26'])
def test_glibc_mangling(self): elf_files = [ self.fake_elf['fake_elf-2.26'], self.fake_elf['fake_elf-2.23'], self.fake_elf['fake_elf-1.1'], ] mangling.handle_glibc_mismatch( elf_files=elf_files, root_path=self.path, core_base_path='/snap/core/current', snap_base_path='/snap/snap-name/current') self.get_packages_mock.assert_called_once_with('libc6') # Everything in the set will be patched regardless of compatibility self.patch_mock.assert_has_calls([ call(elf_file=self.fake_elf['fake_elf-2.26']), call(elf_file=self.fake_elf['fake_elf-2.23']), call(elf_file=self.fake_elf['fake_elf-1.1']), ])
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() # Reset to take into account new data inside prime provided by other # parts. self._soname_cache.reset() 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))) if (linker_incompat or libc6_staged or classic_mangling_needed): if not libc6_staged: raise errors.StagePackageMissingError(package='libc6') handle_glibc_mismatch(elf_files=elf_files, root_path=self.primedir, snap_base_path=self._snap_base_path, core_base_path=core_path, preferred_patchelf_path=staged_patchelf_path, soname_cache=self._soname_cache) elif is_classic: dynamic_linker = self._project_options.get_core_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)
def prime(self, force=False) -> None: 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() for elf_file in elf_files: all_dependencies.update( elf_file.load_dependencies(root_path=self.primedir, core_base_path=core_path)) # Split the necessary dependencies into their corresponding location. # We'll both migrate and track the system dependencies, but we'll only # track the part and staged dependencies, since they should have # already been primed by other means, and migrating them again could # potentially override the `stage` or `snap` filtering. (in_part, staged, primed, system) = _split_dependencies(all_dependencies, self.installdir, self.stagedir, self.primedir) part_dependency_paths = {os.path.dirname(d) for d in in_part} staged_dependency_paths = {os.path.dirname(d) for d in staged} dependency_paths = part_dependency_paths | staged_dependency_paths if not self._build_attributes.no_system_libraries(): system_dependency_paths = {os.path.dirname(d) for d in system} dependency_paths.update(system_dependency_paths) if system: # Lots of dependencies are linked with a symlink, so we need to # make sure we follow those symlinks when we migrate the # dependencies. _migrate_files(system, system_dependency_paths, '/', self.primedir, follow_symlinks=True) formatted_system = '\n'.join(sorted(system)) logger.warning( 'Files from the build host were migrated into the snap to ' 'satisfy dependencies that would otherwise not be met. ' 'This feature will be removed in a future release. If ' 'these libraries are needed in the final snap, ensure ' 'that the following are either satisfied by a ' 'stage-packages entry or through a part:\n{}'.format( formatted_system)) # 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_compatible = (e.is_linker_compatible(linker='ld-2.23.so') for e in elf_files) if not all((x for x in linker_compatible)): handle_glibc_mismatch(elf_files=elf_files, root_path=self.primedir, snap_base_path=self._snap_base_path, core_base_path=core_path, preferred_patchelf_path=staged_patchelf_path) elif self._confinement == 'classic': dynamic_linker = self._project_options.get_core_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)