Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/object/src/build/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 116 kB image not shown  

Quelle  elf.rs   Sprache: unbekannt

 
//! This module provides a [`Builder`] for reading, modifying, and then writing ELF files.
use alloc::vec::Vec;
use core::convert::TryInto;
use core::fmt;
use core::marker::PhantomData;
#[cfg(not(feature = "std"))]
use hashbrown::HashMap;
#[cfg(feature = "std")]
use std::collections::HashMap;

use crate::build::{ByteString, Bytes, Error, Id, IdPrivate, Item, Result, Table};
use crate::elf;
use crate::read::elf::{Dyn, FileHeader, ProgramHeader, Rela, SectionHeader, Sym};
use crate::read::{self, FileKind, ReadRef};
use crate::write;
use crate::Endianness;

/// A builder for reading, modifying, and then writing ELF files.
///
/// Public fields are available for modifying the values that will be written.
/// Methods are available to add elements to tables, and elements can be deleted
/// from tables by setting the `delete` field in the element.
#[derive(Debug)]
pub struct Builder<'data> {
    /// The endianness.
    ///
    /// Used to set the data encoding when writing the ELF file.
    pub endian: Endianness,
    /// Whether file is 64-bit.
    ///
    /// Use to set the file class when writing the ELF file.
    pub is_64: bool,
    /// The alignment of [`elf::PT_LOAD`] segments.
    ///
    /// This is an informational field and is not used when writing the ELF file.
    /// It can optionally be used when calling [`Segments::add_load_segment`].
    ///
    /// It is determined heuristically when reading the ELF file. Currently,
    /// if all load segments have the same alignment, that alignment is used,
    /// otherwise it is set to 1.
    pub load_align: u64,
    /// The file header.
    pub header: Header,
    /// The segment table.
    pub segments: Segments<'data>,
    /// The section table.
    pub sections: Sections<'data>,
    /// The symbol table.
    pub symbols: Symbols<'data>,
    /// The dynamic symbol table.
    pub dynamic_symbols: DynamicSymbols<'data>,
    /// The base version for the GNU version definitions.
    ///
    /// This will be written as a version definition with index 1.
    pub version_base: Option<ByteString<'data>>,
    /// The GNU version definitions and dependencies.
    pub versions: Versions<'data>,
    /// The filenames used in the GNU version definitions.
    pub version_files: VersionFiles<'data>,
    /// The bucket count parameter for the hash table.
    pub hash_bucket_count: u32,
    /// The bloom shift parameter for the GNU hash table.
    pub gnu_hash_bloom_shift: u32,
    /// The bloom count parameter for the GNU hash table.
    pub gnu_hash_bloom_count: u32,
    /// The bucket count parameter for the GNU hash table.
    pub gnu_hash_bucket_count: u32,
    marker: PhantomData<()>,
}

impl<'data> Builder<'data> {
    /// Create a new ELF builder.
    pub fn new(endian: Endianness, is_64: bool) -> Self {
        Self {
            endian,
            is_64,
            load_align: 1,
            header: Header::default(),
            segments: Segments::new(),
            sections: Sections::new(),
            symbols: Symbols::new(),
            dynamic_symbols: Symbols::new(),
            version_base: None,
            versions: Versions::new(),
            version_files: VersionFiles::new(),
            hash_bucket_count: 0,
            gnu_hash_bloom_shift: 0,
            gnu_hash_bloom_count: 0,
            gnu_hash_bucket_count: 0,
            marker: PhantomData,
        }
    }

    /// Read the ELF file from file data.
    pub fn read<R: ReadRef<'data>>(data: R) -> Result<Self> {
        match FileKind::parse(data)? {
            FileKind::Elf32 => Self::read32(data),
            FileKind::Elf64 => Self::read64(data),
            #[allow(unreachable_patterns)]
            _ => Err(Error::new("Not an ELF file")),
        }
    }

    /// Read a 32-bit ELF file from file data.
    pub fn read32<R: ReadRef<'data>>(data: R) -> Result<Self> {
        Self::read_file::<elf::FileHeader32<Endianness>, R>(data)
    }

    /// Read a 64-bit ELF file from file data.
    pub fn read64<R: ReadRef<'data>>(data: R) -> Result<Self> {
        Self::read_file::<elf::FileHeader64<Endianness>, R>(data)
    }

    fn read_file<Elf, R>(data: R) -> Result<Self>
    where
        Elf: FileHeader<Endian = Endianness>,
        R: ReadRef<'data>,
    {
        let header = Elf::parse(data)?;
        let endian = header.endian()?;
        let is_mips64el = header.is_mips64el(endian);
        let section_strings_index = header.section_strings_index(endian, data)?;
        let segments = header.program_headers(endian, data)?;
        let sections = header.sections(endian, data)?;
        let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?;
        let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?;

        let mut builder = Builder {
            endian,
            is_64: header.is_type_64(),
            load_align: 0,
            header: Header {
                os_abi: header.e_ident().os_abi,
                abi_version: header.e_ident().abi_version,
                e_type: header.e_type(endian),
                e_machine: header.e_machine(endian),
                e_entry: header.e_entry(endian).into(),
                e_flags: header.e_flags(endian),
                e_phoff: header.e_phoff(endian).into(),
            },
            segments: Segments::new(),
            sections: Sections::new(),
            symbols: Symbols::new(),
            dynamic_symbols: Symbols::new(),
            version_base: None,
            versions: Versions::new(),
            version_files: VersionFiles::new(),
            hash_bucket_count: 0,
            gnu_hash_bloom_shift: 0,
            gnu_hash_bloom_count: 0,
            gnu_hash_bucket_count: 0,
            marker: PhantomData,
        };

        for segment in segments {
            if segment.p_type(endian) == elf::PT_LOAD {
                let p_align = segment.p_align(endian).into();
                if builder.load_align == 0 {
                    builder.load_align = p_align;
                } else if builder.load_align != p_align {
                    builder.load_align = 1;
                }
            }

            let id = builder.segments.next_id();
            builder.segments.push(Segment {
                id,
                delete: false,
                p_type: segment.p_type(endian),
                p_flags: segment.p_flags(endian),
                p_offset: segment.p_offset(endian).into(),
                p_vaddr: segment.p_vaddr(endian).into(),
                p_paddr: segment.p_paddr(endian).into(),
                p_filesz: segment.p_filesz(endian).into(),
                p_memsz: segment.p_memsz(endian).into(),
                p_align: segment.p_align(endian).into(),
                sections: Vec::new(),
                marker: PhantomData,
            });
        }
        if builder.load_align == 0 {
            builder.load_align = 1;
        }

        for (index, section) in sections.enumerate().skip(1) {
            let id = SectionId(index.0 - 1);
            let relocations = if let Some((rels, link)) = section.rel(endian, data)? {
                Self::read_relocations(
                    index,
                    endian,
                    is_mips64el,
                    section,
                    rels,
                    link,
                    &symbols,
                    &dynamic_symbols,
                )?
            } else if let Some((rels, link)) = section.rela(endian, data)? {
                Self::read_relocations(
                    index,
                    endian,
                    is_mips64el,
                    section,
                    rels,
                    link,
                    &symbols,
                    &dynamic_symbols,
                )?
            } else {
                SectionData::Data(Bytes::default())
            };
            if let Some(hash) = section.hash_header(endian, data)? {
                builder.hash_bucket_count = hash.bucket_count.get(endian);
            }
            if let Some(hash) = section.gnu_hash_header(endian, data)? {
                builder.gnu_hash_bloom_shift = hash.bloom_shift.get(endian);
                builder.gnu_hash_bloom_count = hash.bloom_count.get(endian);
                builder.gnu_hash_bucket_count = hash.bucket_count.get(endian);
            }
            let data = match section.sh_type(endian) {
                elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()),
                elf::SHT_PROGBITS
                | elf::SHT_INIT_ARRAY
                | elf::SHT_FINI_ARRAY
                | elf::SHT_PREINIT_ARRAY => SectionData::Data(section.data(endian, data)?.into()),
                elf::SHT_REL | elf::SHT_RELA => relocations,
                elf::SHT_SYMTAB => {
                    if index == symbols.section() {
                        SectionData::Symbol
                    } else {
                        return Err(Error(format!(
                            "Unsupported SHT_SYMTAB section at index {}",
                            index
                        )));
                    }
                }
                elf::SHT_SYMTAB_SHNDX => {
                    if index == symbols.shndx_section() {
                        SectionData::SymbolSectionIndex
                    } else {
                        return Err(Error(format!(
                            "Unsupported SHT_SYMTAB_SHNDX section at index {}",
                            index
                        )));
                    }
                }
                elf::SHT_DYNSYM => {
                    if index == dynamic_symbols.section() {
                        SectionData::DynamicSymbol
                    } else {
                        return Err(Error(format!(
                            "Unsupported SHT_DYNSYM section at index {}",
                            index
                        )));
                    }
                }
                elf::SHT_STRTAB => {
                    if index == symbols.string_section() {
                        SectionData::String
                    } else if index == dynamic_symbols.string_section() {
                        SectionData::DynamicString
                    } else if index == section_strings_index {
                        SectionData::SectionString
                    } else {
                        return Err(Error(format!(
                            "Unsupported SHT_STRTAB section at index {}",
                            index
                        )));
                    }
                }
                elf::SHT_NOTE => SectionData::Note(section.data(endian, data)?.into()),
                elf::SHT_DYNAMIC => {
                    let (dyns, link) = section.dynamic(endian, data)?.unwrap();
                    let dynamic_strings = sections.strings(endian, data, link)?;
                    Self::read_dynamics::<Elf, _>(endian, dyns, dynamic_strings)?
                }
                elf::SHT_GNU_ATTRIBUTES => {
                    let attributes = section.attributes(endian, data)?;
                    Self::read_attributes(index, attributes, sections.len(), symbols.len())?
                }
                elf::SHT_HASH => SectionData::Hash,
                elf::SHT_GNU_HASH => SectionData::GnuHash,
                elf::SHT_GNU_VERSYM => SectionData::GnuVersym,
                elf::SHT_GNU_VERDEF => SectionData::GnuVerdef,
                elf::SHT_GNU_VERNEED => SectionData::GnuVerneed,
                other => match (builder.header.e_machine, other) {
                    (elf::EM_ARM, elf::SHT_ARM_ATTRIBUTES)
                    | (elf::EM_AARCH64, elf::SHT_AARCH64_ATTRIBUTES)
                    | (elf::EM_CSKY, elf::SHT_CSKY_ATTRIBUTES)
                    | (elf::EM_RISCV, elf::SHT_RISCV_ATTRIBUTES) => {
                        let attributes = section.attributes(endian, data)?;
                        Self::read_attributes(index, attributes, sections.len(), symbols.len())?
                    }
                    // Some section types that we can't parse but that are safe to copy.
                    // Lots of types missing, add as needed. We can't default to copying
                    // everything because some types are not safe to copy.
                    (elf::EM_ARM, elf::SHT_ARM_EXIDX)
                    | (elf::EM_IA_64, elf::SHT_IA_64_UNWIND)
                    | (elf::EM_MIPS, elf::SHT_MIPS_REGINFO)
                    | (elf::EM_MIPS, elf::SHT_MIPS_DWARF)
                    | (elf::EM_X86_64, elf::SHT_X86_64_UNWIND) => {
                        SectionData::Data(section.data(endian, data)?.into())
                    }
                    _ => return Err(Error(format!("Unsupported section type {:x}", other))),
                },
            };
            let sh_flags = section.sh_flags(endian).into();
            let sh_link = section.sh_link(endian);
            let sh_link_section = if sh_link == 0 {
                None
            } else {
                if sh_link as usize >= sections.len() {
                    return Err(Error(format!(
                        "Invalid sh_link {} in section at index {}",
                        sh_link, index
                    )));
                }
                Some(SectionId(sh_link as usize - 1))
            };
            let sh_info = section.sh_info(endian);
            let sh_info_section = if sh_info == 0 || sh_flags & u64::from(elf::SHF_INFO_LINK) == 0 {
                None
            } else {
                if sh_info as usize >= sections.len() {
                    return Err(Error(format!(
                        "Invalid sh_info link {} in section at index {}",
                        sh_info, index
                    )));
                }
                Some(SectionId(sh_info as usize - 1))
            };
            let sh_flags = section.sh_flags(endian).into();
            let sh_addr = section.sh_addr(endian).into();
            if sh_flags & u64::from(elf::SHF_ALLOC) != 0 {
                for segment in &mut builder.segments {
                    if segment.contains_address(sh_addr) {
                        segment.sections.push(id);
                    }
                }
            }
            builder.sections.push(Section {
                id,
                delete: false,
                name: sections.section_name(endian, section)?.into(),
                sh_type: section.sh_type(endian),
                sh_flags,
                sh_addr,
                sh_offset: section.sh_offset(endian).into(),
                sh_size: section.sh_size(endian).into(),
                sh_link_section,
                sh_info,
                sh_info_section,
                sh_addralign: section.sh_addralign(endian).into(),
                sh_entsize: section.sh_entsize(endian).into(),
                data,
            });
        }

        Self::read_symbols(
            endian,
            &symbols,
            &mut builder.symbols,
            builder.sections.len(),
        )?;
        Self::read_symbols(
            endian,
            &dynamic_symbols,
            &mut builder.dynamic_symbols,
            builder.sections.len(),
        )?;
        builder.read_gnu_versions(endian, data, §ions, &dynamic_symbols)?;

        Ok(builder)
    }

    #[allow(clippy::too_many_arguments)]
    fn read_relocations<Elf, Rel, R>(
        index: read::SectionIndex,
        endian: Elf::Endian,
        is_mips64el: bool,
        section: &'data Elf::SectionHeader,
        rels: &'data [Rel],
        link: read::SectionIndex,
        symbols: &read::elf::SymbolTable<'data, Elf, R>,
        dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>,
    ) -> Result<SectionData<'data>>
    where
        Elf: FileHeader<Endian = Endianness>,
        Rel: Copy + Into<Elf::Rela>,
        R: ReadRef<'data>,
    {
        if link == dynamic_symbols.section() {
            Self::read_relocations_impl::<Elf, Rel, true>(
                index,
                endian,
                is_mips64el,
                rels,
                dynamic_symbols.len(),
            )
            .map(SectionData::DynamicRelocation)
        } else if link.0 == 0 || section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 {
            // If there's no link, then none of the relocations may reference symbols.
            // Assume that these are dynamic relocations, but don't use the dynamic
            // symbol table when parsing.
            //
            // Additionally, sometimes there is an allocated section that links to
            // the static symbol table. We don't currently support this case in general,
            // but if none of the relocation entries reference a symbol then it is
            // safe to treat it as a dynamic relocation section.
            //
            // For both of these cases, if there is a reference to a symbol then
            // an error will be returned when parsing the relocations.
            Self::read_relocations_impl::<Elf, Rel, true>(index, endian, is_mips64el, rels, 0)
                .map(SectionData::DynamicRelocation)
        } else if link == symbols.section() {
            Self::read_relocations_impl::<Elf, Rel, false>(
                index,
                endian,
                is_mips64el,
                rels,
                symbols.len(),
            )
            .map(SectionData::Relocation)
        } else {
            return Err(Error(format!(
                "Invalid sh_link {} in relocation section at index {}",
                link.0, index,
            )));
        }
    }

    fn read_relocations_impl<Elf, Rel, const DYNAMIC: bool>(
        index: read::SectionIndex,
        endian: Elf::Endian,
        is_mips64el: bool,
        rels: &'data [Rel],
        symbols_len: usize,
    ) -> Result<Vec<Relocation<DYNAMIC>>>
    where
        Elf: FileHeader<Endian = Endianness>,
        Rel: Copy + Into<Elf::Rela>,
    {
        let mut relocations = Vec::new();
        for rel in rels {
            let rel = (*rel).into();
            let symbol = if let Some(symbol) = rel.symbol(endian, is_mips64el) {
                if symbol.0 >= symbols_len {
                    return Err(Error(format!(
                        "Invalid symbol index {} in relocation section at index {}",
                        symbol, index,
                    )));
                }
                Some(SymbolId(symbol.0 - 1))
            } else {
                None
            };
            relocations.push(Relocation {
                r_offset: rel.r_offset(endian).into(),
                symbol,
                r_type: rel.r_type(endian, is_mips64el),
                r_addend: rel.r_addend(endian).into(),
            });
        }
        Ok(relocations)
    }

    fn read_dynamics<Elf, R>(
        endian: Elf::Endian,
        dyns: &'data [Elf::Dyn],
        strings: read::StringTable<'data, R>,
    ) -> Result<SectionData<'data>>
    where
        Elf: FileHeader<Endian = Endianness>,
        R: ReadRef<'data>,
    {
        let mut dynamics = Vec::with_capacity(dyns.len());
        for d in dyns {
            let tag = d.d_tag(endian).into().try_into().map_err(|_| {
                Error(format!(
                    "Unsupported dynamic tag 0x{:x}",
                    d.d_tag(endian).into()
                ))
            })?;
            if tag == elf::DT_NULL {
                break;
            }
            let val = d.d_val(endian).into();
            dynamics.push(if d.is_string(endian) {
                let val =
                    strings
                        .get(val.try_into().map_err(|_| {
                            Error(format!("Unsupported dynamic string 0x{:x}", val))
                        })?)
                        .map_err(|_| Error(format!("Invalid dynamic string 0x{:x}", val)))?;
                Dynamic::String {
                    tag,
                    val: val.into(),
                }
            } else {
                match tag {
                    elf::DT_SYMTAB
                    | elf::DT_STRTAB
                    | elf::DT_STRSZ
                    | elf::DT_HASH
                    | elf::DT_GNU_HASH
                    | elf::DT_VERSYM
                    | elf::DT_VERDEF
                    | elf::DT_VERDEFNUM
                    | elf::DT_VERNEED
                    | elf::DT_VERNEEDNUM => Dynamic::Auto { tag },
                    _ => Dynamic::Integer { tag, val },
                }
            });
        }
        Ok(SectionData::Dynamic(dynamics))
    }

    fn read_symbols<Elf, R, const DYNAMIC: bool>(
        endian: Elf::Endian,
        symbols: &read::elf::SymbolTable<'data, Elf, R>,
        builder_symbols: &mut Symbols<'data, DYNAMIC>,
        sections_len: usize,
    ) -> Result<()>
    where
        Elf: FileHeader<Endian = Endianness>,
        R: ReadRef<'data>,
    {
        for (index, symbol) in symbols.enumerate().skip(1) {
            let id = SymbolId(index.0 - 1);
            let section =
                if let Some(section_index) = symbols.symbol_section(endian, symbol, index)? {
                    let section_id = section_index.0.wrapping_sub(1);
                    if section_id >= sections_len {
                        return Err(Error::new("Invalid symbol section index"));
                    }
                    Some(SectionId(section_id))
                } else {
                    None
                };
            builder_symbols.push(Symbol {
                id,
                delete: false,
                name: symbols.symbol_name(endian, symbol)?.into(),
                section,
                st_info: symbol.st_info(),
                st_other: symbol.st_other(),
                st_shndx: symbol.st_shndx(endian),
                st_value: symbol.st_value(endian).into(),
                st_size: symbol.st_size(endian).into(),
                version: VersionId::local(),
                version_hidden: false,
            });
        }
        Ok(())
    }

    fn read_attributes<Elf>(
        index: read::SectionIndex,
        attributes: read::elf::AttributesSection<'data, Elf>,
        sections_len: usize,
        symbols_len: usize,
    ) -> Result<SectionData<'data>>
    where
        Elf: FileHeader<Endian = Endianness>,
    {
        let mut builder_attributes = AttributesSection::new();
        let mut subsections = attributes.subsections()?;
        while let Some(subsection) = subsections.next()? {
            let mut builder_subsection = AttributesSubsection::new(subsection.vendor().into());
            let mut subsubsections = subsection.subsubsections();
            while let Some(subsubsection) = subsubsections.next()? {
                let tag = match subsubsection.tag() {
                    elf::Tag_File => AttributeTag::File,
                    elf::Tag_Section => {
                        let mut tag_sections = Vec::new();
                        let mut indices = subsubsection.indices();
                        while let Some(index) = indices.next()? {
                            let index = index as usize;
                            if index >= sections_len {
                                return Err(Error(format!(
                                    "Invalid section index {} in attribute",
                                    index
                                )));
                            }
                            tag_sections.push(SectionId(index - 1));
                        }
                        AttributeTag::Section(tag_sections)
                    }
                    elf::Tag_Symbol => {
                        let mut tag_symbols = Vec::new();
                        let mut indices = subsubsection.indices();
                        while let Some(index) = indices.next()? {
                            let index = index as usize;
                            if index >= symbols_len {
                                return Err(Error(format!(
                                    "Invalid symbol index {} in attribute",
                                    index
                                )));
                            }
                            tag_symbols.push(SymbolId(index - 1));
                        }
                        AttributeTag::Symbol(tag_symbols)
                    }
                    tag => {
                        return Err(Error(format!(
                            "Unsupported attribute tag 0x{:x} in section at index {}",
                            tag, index,
                        )))
                    }
                };
                let data = subsubsection.attributes_data().into();
                builder_subsection
                    .subsubsections
                    .push(AttributesSubsubsection { tag, data });
            }
            builder_attributes.subsections.push(builder_subsection);
        }
        Ok(SectionData::Attributes(builder_attributes))
    }

    fn read_gnu_versions<Elf, R>(
        &mut self,
        endian: Elf::Endian,
        data: R,
        sections: &read::elf::SectionTable<'data, Elf, R>,
        dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>,
    ) -> Result<()>
    where
        Elf: FileHeader<Endian = Endianness>,
        R: ReadRef<'data>,
    {
        let strings = dynamic_symbols.strings();
        let mut ids = HashMap::new();
        ids.insert(0, VersionId::local());
        ids.insert(1, VersionId::global());

        if let Some((mut verdefs, link)) = sections.gnu_verdef(endian, data)? {
            if link != dynamic_symbols.string_section() {
                return Err(Error::new("Invalid SHT_GNU_VERDEF section"));
            }
            while let Some((verdef, mut verdauxs)) = verdefs.next()? {
                let flags = verdef.vd_flags.get(endian);
                if flags & elf::VER_FLG_BASE != 0 {
                    if flags != elf::VER_FLG_BASE
                        || verdef.vd_ndx.get(endian) != 1
                        || verdef.vd_cnt.get(endian) != 1
                    {
                        return Err(Error::new("Unsupported VER_FLG_BASE in SHT_GNU_VERDEF"));
                    }
                    if self.version_base.is_some() {
                        return Err(Error::new("Duplicate VER_FLG_BASE in SHT_GNU_VERDEF"));
                    }
                    let verdaux = verdauxs.next()?.ok_or_else(|| {
                        Error::new("Missing name for VER_FLG_BASE in SHT_GNU_VERDEF")
                    })?;
                    self.version_base = Some(verdaux.name(endian, strings)?.into());
                    continue;
                }

                let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION;
                let id = self.versions.next_id();
                if ids.insert(index, id).is_some() {
                    return Err(Error(format!("Duplicate SHT_GNU_VERDEF index {}", index)));
                }

                let mut names = Vec::new();
                while let Some(verdaux) = verdauxs.next()? {
                    names.push(verdaux.name(endian, strings)?.into());
                }

                let data = VersionData::Def(VersionDef { flags, names });
                self.versions.push(Version {
                    id,
                    delete: false,
                    data,
                });
            }
        }

        if let Some((mut verneeds, link)) = sections.gnu_verneed(endian, data)? {
            if link != dynamic_symbols.string_section() {
                return Err(Error::new("Invalid SHT_GNU_VERNEED section"));
            }
            while let Some((verneed, mut vernauxs)) = verneeds.next()? {
                let file = VersionFileId(self.version_files.len());
                self.version_files.push(VersionFile {
                    id: file,
                    delete: false,
                    name: verneed.file(endian, strings)?.into(),
                });
                while let Some(vernaux) = vernauxs.next()? {
                    let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION;
                    let id = self.versions.next_id();
                    if ids.insert(index, id).is_some() {
                        return Err(Error(format!("Duplicate SHT_GNU_VERNEED index {}", index)));
                    }

                    let data = VersionData::Need(VersionNeed {
                        flags: vernaux.vna_flags.get(endian),
                        name: vernaux.name(endian, strings)?.into(),
                        file,
                    });
                    self.versions.push(Version {
                        id,
                        delete: false,
                        data,
                    });
                }
            }
        }

        if let Some((versyms, link)) = sections.gnu_versym(endian, data)? {
            if versyms.len() != dynamic_symbols.len() || link != dynamic_symbols.section() {
                return Err(Error::new("Invalid SHT_GNU_VERSYM section"));
            }
            for (id, versym) in versyms.iter().skip(1).enumerate() {
                let index = versym.0.get(endian);
                let symbol = self.dynamic_symbols.get_mut(SymbolId(id));
                symbol.version = *ids
                    .get(&(index & elf::VERSYM_VERSION))
                    .ok_or_else(|| Error(format!("Invalid SHT_GNU_VERSYM index {:x}", index)))?;
                symbol.version_hidden = index & elf::VERSYM_HIDDEN != 0;
            }
        }
        Ok(())
    }

    /// Write the ELF file to the buffer.
    pub fn write(mut self, buffer: &mut dyn write::WritableBuffer) -> Result<()> {
        struct SectionOut {
            id: SectionId,
            name: Option<write::StringId>,
            offset: usize,
            attributes: Vec<u8>,
        }

        struct SymbolOut {
            id: SymbolId,
            name: Option<write::StringId>,
        }

        struct DynamicSymbolOut {
            id: DynamicSymbolId,
            name: Option<write::StringId>,
            hash: Option<u32>,
            gnu_hash: Option<u32>,
        }

        #[derive(Default, Clone)]
        struct VersionFileOut {
            versions: Vec<VersionId>,
        }

        // TODO: require the caller to do this?
        self.delete_orphans();
        self.delete_unused_versions();

        let mut writer = write::elf::Writer::new(self.endian, self.is_64, buffer);

        // Find metadata sections, and assign section indices.
        let mut shstrtab_id = None;
        let mut symtab_id = None;
        let mut symtab_shndx_id = None;
        let mut strtab_id = None;
        let mut dynsym_id = None;
        let mut dynstr_id = None;
        let mut hash_id = None;
        let mut gnu_hash_id = None;
        let mut gnu_versym_id = None;
        let mut gnu_verdef_id = None;
        let mut gnu_verneed_id = None;
        let mut out_sections = Vec::with_capacity(self.sections.len());
        let mut out_sections_index = vec![None; self.sections.len()];
        if !self.sections.is_empty() {
            writer.reserve_null_section_index();
        }
        for section in &self.sections {
            let index = match §ion.data {
                SectionData::Data(_)
                | SectionData::UninitializedData(_)
                | SectionData::Relocation(_)
                | SectionData::DynamicRelocation(_)
                | SectionData::Note(_)
                | SectionData::Dynamic(_)
                | SectionData::Attributes(_) => writer.reserve_section_index(),
                SectionData::SectionString => {
                    if shstrtab_id.is_some() {
                        return Err(Error::new("Multiple .shstrtab sections"));
                    }
                    shstrtab_id = Some(section.id);
                    writer.reserve_shstrtab_section_index_with_name(§ion.name)
                }
                SectionData::Symbol => {
                    if symtab_id.is_some() {
                        return Err(Error::new("Multiple .symtab sections"));
                    }
                    symtab_id = Some(section.id);
                    writer.reserve_symtab_section_index_with_name(§ion.name)
                }
                SectionData::SymbolSectionIndex => {
                    if symtab_shndx_id.is_some() {
                        return Err(Error::new("Multiple .symtab_shndx sections"));
                    }
                    symtab_shndx_id = Some(section.id);
                    writer.reserve_symtab_shndx_section_index_with_name(§ion.name)
                }
                SectionData::String => {
                    if strtab_id.is_some() {
                        return Err(Error::new("Multiple .strtab sections"));
                    }
                    strtab_id = Some(section.id);
                    writer.reserve_strtab_section_index_with_name(§ion.name)
                }
                SectionData::DynamicSymbol => {
                    if dynsym_id.is_some() {
                        return Err(Error::new("Multiple .dynsym sections"));
                    }
                    dynsym_id = Some(section.id);
                    writer.reserve_dynsym_section_index_with_name(§ion.name)
                }
                SectionData::DynamicString => {
                    if dynstr_id.is_some() {
                        return Err(Error::new("Multiple .dynstr sections"));
                    }
                    dynstr_id = Some(section.id);
                    writer.reserve_dynstr_section_index_with_name(§ion.name)
                }
                SectionData::Hash => {
                    if hash_id.is_some() {
                        return Err(Error::new("Multiple .hash sections"));
                    }
                    hash_id = Some(section.id);
                    writer.reserve_hash_section_index_with_name(§ion.name)
                }
                SectionData::GnuHash => {
                    if gnu_hash_id.is_some() {
                        return Err(Error::new("Multiple .gnu.hash sections"));
                    }
                    gnu_hash_id = Some(section.id);
                    writer.reserve_gnu_hash_section_index_with_name(§ion.name)
                }
                SectionData::GnuVersym => {
                    if gnu_versym_id.is_some() {
                        return Err(Error::new("Multiple .gnu.version sections"));
                    }
                    gnu_versym_id = Some(section.id);
                    writer.reserve_gnu_versym_section_index_with_name(§ion.name)
                }
                SectionData::GnuVerdef => {
                    if gnu_verdef_id.is_some() {
                        return Err(Error::new("Multiple .gnu.version_d sections"));
                    }
                    gnu_verdef_id = Some(section.id);
                    writer.reserve_gnu_verdef_section_index_with_name(§ion.name)
                }
                SectionData::GnuVerneed => {
                    if gnu_verneed_id.is_some() {
                        return Err(Error::new("Multiple .gnu.version_r sections"));
                    }
                    gnu_verneed_id = Some(section.id);
                    writer.reserve_gnu_verneed_section_index_with_name(§ion.name)
                }
            };
            out_sections_index[section.id.0] = Some(index);

            let name = if section.name.is_empty() {
                None
            } else {
                Some(writer.add_section_name(§ion.name))
            };
            out_sections.push(SectionOut {
                id: section.id,
                name,
                offset: 0,
                attributes: Vec::new(),
            });
        }

        // Assign dynamic strings.
        for section in &self.sections {
            if let SectionData::Dynamic(dynamics) = §ion.data {
                for dynamic in dynamics {
                    if let Dynamic::String { val, .. } = dynamic {
                        writer.add_dynamic_string(val);
                    }
                }
            }
        }

        // Assign dynamic symbol indices.
        let mut out_dynsyms = Vec::with_capacity(self.dynamic_symbols.len());
        // Local symbols must come before global.
        let local_symbols = self
            .dynamic_symbols
            .into_iter()
            .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL);
        let global_symbols = self
            .dynamic_symbols
            .into_iter()
            .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL);
        for symbol in local_symbols.chain(global_symbols) {
            let mut name = None;
            let mut hash = None;
            let mut gnu_hash = None;
            if !symbol.name.is_empty() {
                name = Some(writer.add_dynamic_string(&symbol.name));
                if hash_id.is_some() {
                    hash = Some(elf::hash(&symbol.name));
                }
                if gnu_hash_id.is_some()
                    && (symbol.section.is_some() || symbol.st_shndx != elf::SHN_UNDEF)
                {
                    gnu_hash = Some(elf::gnu_hash(&symbol.name));
                }
            }
            out_dynsyms.push(DynamicSymbolOut {
                id: symbol.id,
                name,
                hash,
                gnu_hash,
            });
        }
        let num_local_dynamic = out_dynsyms
            .iter()
            .take_while(|sym| self.dynamic_symbols.get(sym.id).st_bind() == elf::STB_LOCAL)
            .count();
        // We must sort for GNU hash before allocating symbol indices.
        let mut gnu_hash_symbol_count = 0;
        if gnu_hash_id.is_some() {
            if self.gnu_hash_bucket_count == 0 {
                return Err(Error::new(".gnu.hash bucket count is zero"));
            }
            // TODO: recalculate bucket_count?
            out_dynsyms[num_local_dynamic..].sort_by_key(|sym| match sym.gnu_hash {
                None => (0, 0),
                Some(hash) => (1, hash % self.gnu_hash_bucket_count),
            });
            gnu_hash_symbol_count = out_dynsyms
                .iter()
                .skip(num_local_dynamic)
                .skip_while(|sym| sym.gnu_hash.is_none())
                .count() as u32;
        }
        let mut out_dynsyms_index = vec![None; self.dynamic_symbols.len()];
        if dynsym_id.is_some() {
            writer.reserve_null_dynamic_symbol_index();
        }
        for out_dynsym in &mut out_dynsyms {
            out_dynsyms_index[out_dynsym.id.0] = Some(writer.reserve_dynamic_symbol_index());
        }

        // Hash parameters.
        let hash_index_base = 1; // Null symbol.
        let hash_chain_count = hash_index_base + out_dynsyms.len() as u32;

        // GNU hash parameters.
        let gnu_hash_index_base = if gnu_hash_symbol_count == 0 {
            0
        } else {
            out_dynsyms.len() as u32 - gnu_hash_symbol_count
        };
        let gnu_hash_symbol_base = gnu_hash_index_base + 1; // Null symbol.

        // Assign symbol indices.
        let mut out_syms = Vec::with_capacity(self.symbols.len());
        // Local symbols must come before global.
        let local_symbols = self
            .symbols
            .into_iter()
            .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL);
        let global_symbols = self
            .symbols
            .into_iter()
            .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL);
        for symbol in local_symbols.chain(global_symbols) {
            let name = if symbol.name.is_empty() {
                None
            } else {
                Some(writer.add_string(&symbol.name))
            };

            out_syms.push(SymbolOut {
                id: symbol.id,
                name,
            });
        }
        let num_local = out_syms
            .iter()
            .take_while(|sym| self.symbols.get(sym.id).st_bind() == elf::STB_LOCAL)
            .count();
        let mut out_syms_index = vec![None; self.symbols.len()];
        if symtab_id.is_some() {
            writer.reserve_null_symbol_index();
        }
        for out_sym in out_syms.iter_mut() {
            out_syms_index[out_sym.id.0] = Some(writer.reserve_symbol_index(None));
        }

        // Count the versions and add version strings.
        let mut verdef_count = 0;
        let mut verdaux_count = 0;
        let mut verdef_shared_base = false;
        let mut verneed_count = 0;
        let mut vernaux_count = 0;
        let mut out_version_files = vec![VersionFileOut::default(); self.version_files.len()];
        if let Some(version_base) = &self.version_base {
            verdef_count += 1;
            verdaux_count += 1;
            writer.add_dynamic_string(version_base);
        }
        for version in &self.versions {
            match &version.data {
                VersionData::Def(def) => {
                    if def.is_shared(verdef_count, self.version_base.as_ref()) {
                        verdef_shared_base = true;
                    } else {
                        verdaux_count += def.names.len();
                        for name in &def.names {
                            writer.add_dynamic_string(name);
                        }
                    }
                    verdef_count += 1;
                }
                VersionData::Need(need) => {
                    vernaux_count += 1;
                    writer.add_dynamic_string(&need.name);
                    out_version_files[need.file.0].versions.push(version.id);
                }
            }
        }
        for file in &self.version_files {
            verneed_count += 1;
            writer.add_dynamic_string(&file.name);
        }

        // Build the attributes sections.
        for out_section in &mut out_sections {
            let SectionData::Attributes(attributes) = &self.sections.get(out_section.id).data
            else {
                continue;
            };
            if attributes.subsections.is_empty() {
                continue;
            }
            let mut writer = writer.attributes_writer();
            for subsection in &attributes.subsections {
                writer.start_subsection(&subsection.vendor);
                for subsubsection in &subsection.subsubsections {
                    writer.start_subsubsection(subsubsection.tag.tag());
                    match &subsubsection.tag {
                        AttributeTag::File => {}
                        AttributeTag::Section(sections) => {
                            for id in sections {
                                if let Some(index) = out_sections_index[id.0] {
                                    writer.write_subsubsection_index(index.0);
                                }
                            }
                            writer.write_subsubsection_index(0);
                        }
                        AttributeTag::Symbol(symbols) => {
                            for id in symbols {
                                if let Some(index) = out_syms_index[id.0] {
                                    writer.write_subsubsection_index(index.0);
                                }
                            }
                            writer.write_subsubsection_index(0);
                        }
                    }
                    writer.write_subsubsection_attributes(&subsubsection.data);
                    writer.end_subsubsection();
                }
                writer.end_subsection();
            }
            out_section.attributes = writer.data();
        }

        // TODO: support section headers in strtab
        if shstrtab_id.is_none() && !out_sections.is_empty() {
            return Err(Error::new(".shstrtab section is needed but not present"));
        }
        if symtab_id.is_none() && !out_syms.is_empty() {
            return Err(Error::new(".symtab section is needed but not present"));
        }
        if symtab_shndx_id.is_none() && writer.symtab_shndx_needed() {
            return Err(Error::new(
                ".symtab.shndx section is needed but not present",
            ));
        } else if symtab_shndx_id.is_some() {
            writer.require_symtab_shndx();
        }
        if strtab_id.is_none() && writer.strtab_needed() {
            return Err(Error::new(".strtab section is needed but not present"));
        } else if strtab_id.is_some() {
            writer.require_strtab();
        }
        if dynsym_id.is_none() && !out_dynsyms.is_empty() {
            return Err(Error::new(".dynsym section is needed but not present"));
        }
        if dynstr_id.is_none() && writer.dynstr_needed() {
            return Err(Error::new(".dynstr section is needed but not present"));
        } else if dynstr_id.is_some() {
            writer.require_dynstr();
        }
        if gnu_verdef_id.is_none() && verdef_count > 0 {
            return Err(Error::new(
                ".gnu.version_d section is needed but not present",
            ));
        }
        if gnu_verneed_id.is_none() && verneed_count > 0 {
            return Err(Error::new(
                ".gnu.version_r section is needed but not present",
            ));
        }

        // Start reserving file ranges.
        writer.reserve_file_header();

        let mut dynsym_addr = None;
        let mut dynstr_addr = None;
        let mut hash_addr = None;
        let mut gnu_hash_addr = None;
        let mut versym_addr = None;
        let mut verdef_addr = None;
        let mut verneed_addr = None;

        if !self.segments.is_empty() {
            // TODO: support program headers in other locations.
            if self.header.e_phoff != writer.reserved_len() as u64 {
                return Err(Error(format!(
                    "Unsupported e_phoff value 0x{:x}",
                    self.header.e_phoff
                )));
            }
            writer.reserve_program_headers(self.segments.count() as u32);
        }

        let mut alloc_sections = Vec::new();
        if !self.segments.is_empty() {
            // Reserve alloc sections at original offsets.
            alloc_sections = out_sections
                .iter()
                .enumerate()
                .filter_map(|(index, out_section)| {
                    let section = self.sections.get(out_section.id);
                    if section.is_alloc() {
                        Some(index)
                    } else {
                        None
                    }
                })
                .collect();
            // The data for alloc sections may need to be written in a different order
            // from their section headers.
            alloc_sections.sort_by_key(|index| {
                let section = &self.sections.get(out_sections[*index].id);
                // Empty sections need to come before other sections at the same offset.
                (section.sh_offset, section.sh_size)
            });
            for index in &alloc_sections {
                let out_section = &mut out_sections[*index];
                let section = &self.sections.get(out_section.id);

                if section.sh_type == elf::SHT_NOBITS {
                    // sh_offset is meaningless for SHT_NOBITS, so preserve the input
                    // value without checking it.
                    out_section.offset = section.sh_offset as usize;
                    continue;
                }

                if section.sh_offset < writer.reserved_len() as u64 {
                    return Err(Error(format!(
                        "Unsupported sh_offset value 0x{:x} for section '{}', expected at least 0x{:x}",
                        section.sh_offset,
                        section.name,
                        writer.reserved_len(),
                    )));
                }
                // The input sh_offset needs to be preserved so that offsets in program
                // headers are correct.
                writer.reserve_until(section.sh_offset as usize);
                out_section.offset = match §ion.data {
                    SectionData::Data(data) => {
                        writer.reserve(data.len(), section.sh_addralign as usize)
                    }
                    SectionData::DynamicRelocation(relocations) => writer
                        .reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA),
                    SectionData::Note(data) => {
                        writer.reserve(data.len(), section.sh_addralign as usize)
                    }
                    SectionData::Dynamic(dynamics) => writer.reserve_dynamics(1 + dynamics.len()),
                    SectionData::DynamicSymbol => {
                        dynsym_addr = Some(section.sh_addr);
                        writer.reserve_dynsym()
                    }
                    SectionData::DynamicString => {
                        dynstr_addr = Some(section.sh_addr);
                        writer.reserve_dynstr()
                    }
                    SectionData::Hash => {
                        hash_addr = Some(section.sh_addr);
                        writer.reserve_hash(self.hash_bucket_count, hash_chain_count)
                    }
                    SectionData::GnuHash => {
                        gnu_hash_addr = Some(section.sh_addr);
                        writer.reserve_gnu_hash(
                            self.gnu_hash_bloom_count,
                            self.gnu_hash_bucket_count,
                            gnu_hash_symbol_count,
                        )
                    }
                    SectionData::GnuVersym => {
                        versym_addr = Some(section.sh_addr);
                        writer.reserve_gnu_versym()
                    }
                    SectionData::GnuVerdef => {
                        verdef_addr = Some(section.sh_addr);
                        writer.reserve_gnu_verdef(verdef_count, verdaux_count)
                    }
                    SectionData::GnuVerneed => {
                        verneed_addr = Some(section.sh_addr);
                        writer.reserve_gnu_verneed(verneed_count, vernaux_count)
                    }
                    _ => {
                        return Err(Error(format!(
                            "Unsupported alloc section type {:x} for section '{}'",
                            section.sh_type, section.name,
                        )));
                    }
                };
                if out_section.offset as u64 != section.sh_offset {
                    return Err(Error(format!(
                        "Unaligned sh_offset value 0x{:x} for section '{}', expected 0x{:x}",
                        section.sh_offset, section.name, out_section.offset,
                    )));
                }
            }
        }

        // Reserve non-alloc sections at any offset.
        for out_section in &mut out_sections {
            let section = self.sections.get(out_section.id);
            if !self.segments.is_empty() && section.is_alloc() {
                continue;
            }
            out_section.offset = match §ion.data {
                SectionData::Data(data) => {
                    writer.reserve(data.len(), section.sh_addralign as usize)
                }
                SectionData::UninitializedData(_) => writer.reserved_len(),
                SectionData::Note(data) => {
                    writer.reserve(data.len(), section.sh_addralign as usize)
                }
                SectionData::Attributes(_) => {
                    writer.reserve(out_section.attributes.len(), section.sh_addralign as usize)
                }
                // These are handled elsewhere.
                SectionData::Relocation(_)
                | SectionData::SectionString
                | SectionData::Symbol
                | SectionData::SymbolSectionIndex
                | SectionData::String => {
                    continue;
                }
                _ => {
                    return Err(Error(format!(
                        "Unsupported non-alloc section type {:x}",
                        section.sh_type
                    )));
                }
            };
        }

        writer.reserve_symtab();
        writer.reserve_symtab_shndx();
        writer.reserve_strtab();

        // Reserve non-alloc relocations.
        for out_section in &mut out_sections {
            let section = self.sections.get(out_section.id);
            if !self.segments.is_empty() && section.is_alloc() {
                continue;
            }
            let SectionData::Relocation(relocations) = §ion.data else {
                continue;
            };
            out_section.offset =
                writer.reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA);
        }

        writer.reserve_shstrtab();
        writer.reserve_section_headers();

        // Start writing.
        writer.write_file_header(&write::elf::FileHeader {
            os_abi: self.header.os_abi,
            abi_version: self.header.abi_version,
            e_type: self.header.e_type,
            e_machine: self.header.e_machine,
            e_entry: self.header.e_entry,
            e_flags: self.header.e_flags,
        })?;

        if !self.segments.is_empty() {
            writer.write_align_program_headers();
            for segment in &self.segments {
                writer.write_program_header(&write::elf::ProgramHeader {
                    p_type: segment.p_type,
                    p_flags: segment.p_flags,
                    p_offset: segment.p_offset,
                    p_vaddr: segment.p_vaddr,
                    p_paddr: segment.p_paddr,
                    p_filesz: segment.p_filesz,
                    p_memsz: segment.p_memsz,
                    p_align: segment.p_align,
                });
            }
        }

        // Write alloc sections.
        if !self.segments.is_empty() {
            for index in &alloc_sections {
                let out_section = &mut out_sections[*index];
                let section = self.sections.get(out_section.id);

                if section.sh_type == elf::SHT_NOBITS {
                    continue;
                }

                writer.pad_until(out_section.offset);
                match §ion.data {
                    SectionData::Data(data) => {
                        writer.write(data);
                    }
                    SectionData::DynamicRelocation(relocations) => {
                        for rel in relocations {
                            let r_sym = if let Some(symbol) = rel.symbol {
                                out_dynsyms_index[symbol.0].unwrap().0
                            } else {
                                0
                            };
                            writer.write_relocation(
                                section.sh_type == elf::SHT_RELA,
                                &write::elf::Rel {
                                    r_offset: rel.r_offset,
                                    r_sym,
                                    r_type: rel.r_type,
                                    r_addend: rel.r_addend,
                                },
                            );
                        }
                    }
                    SectionData::Note(data) => {
                        writer.write(data);
                    }
                    SectionData::Dynamic(dynamics) => {
                        for d in dynamics {
                            match *d {
                                Dynamic::Auto { tag } => {
                                    // TODO: support more values
                                    let val = match tag {
                                        elf::DT_SYMTAB => dynsym_addr.ok_or(Error::new(
                                            "Missing .dynsym section for DT_SYMTAB",
                                        ))?,
                                        elf::DT_STRTAB => dynstr_addr.ok_or(Error::new(
                                            "Missing .dynstr section for DT_STRTAB",
                                        ))?,
                                        elf::DT_STRSZ => writer.dynstr_len() as u64,
                                        elf::DT_HASH => hash_addr.ok_or(Error::new(
                                            "Missing .hash section for DT_HASH",
                                        ))?,
                                        elf::DT_GNU_HASH => gnu_hash_addr.ok_or(Error::new(
                                            "Missing .gnu.hash section for DT_GNU_HASH",
                                        ))?,
                                        elf::DT_VERSYM => versym_addr.ok_or(Error::new(
                                            "Missing .gnu.version section for DT_VERSYM",
                                        ))?,
                                        elf::DT_VERDEF => verdef_addr.ok_or(Error::new(
                                            "Missing .gnu.version_d section for DT_VERDEF",
                                        ))?,
                                        elf::DT_VERDEFNUM => verdef_count as u64,
                                        elf::DT_VERNEED => verneed_addr.ok_or(Error::new(
                                            "Missing .gnu.version_r section for DT_VERNEED",
                                        ))?,
                                        elf::DT_VERNEEDNUM => verneed_count as u64,
                                        _ => {
                                            return Err(Error(format!(
                                                "Cannot generate value for dynamic tag 0x{:x}",
                                                tag
                                            )))
                                        }
                                    };
                                    writer.write_dynamic(tag, val);
                                }
                                Dynamic::Integer { tag, val } => {
                                    writer.write_dynamic(tag, val);
                                }
                                Dynamic::String { tag, ref val } => {
                                    let val = writer.get_dynamic_string(val);
                                    writer.write_dynamic_string(tag, val);
                                }
                            }
                        }
                        writer.write_dynamic(elf::DT_NULL, 0);
                    }
                    SectionData::DynamicSymbol => {
                        writer.write_null_dynamic_symbol();
                        for out_dynsym in &out_dynsyms {
                            let symbol = self.dynamic_symbols.get(out_dynsym.id);
                            let section =
                                symbol.section.map(|id| out_sections_index[id.0].unwrap());
                            writer.write_dynamic_symbol(&write::elf::Sym {
                                name: out_dynsym.name,
                                section,
                                st_info: symbol.st_info,
                                st_other: symbol.st_other,
                                st_shndx: symbol.st_shndx,
                                st_value: symbol.st_value,
                                st_size: symbol.st_size,
                            });
                        }
                    }
                    SectionData::DynamicString => {
                        writer.write_dynstr();
                    }
                    SectionData::Hash => {
                        if self.hash_bucket_count == 0 {
                            return Err(Error::new(".hash bucket count is zero"));
                        }
                        writer.write_hash(self.hash_bucket_count, hash_chain_count, |index| {
                            out_dynsyms
                                .get(index.checked_sub(hash_index_base)? as usize)?
                                .hash
                        });
                    }
                    SectionData::GnuHash => {
                        if self.gnu_hash_bucket_count == 0 {
                            return Err(Error::new(".gnu.hash bucket count is zero"));
                        }
                        writer.write_gnu_hash(
                            gnu_hash_symbol_base,
                            self.gnu_hash_bloom_shift,
                            self.gnu_hash_bloom_count,
                            self.gnu_hash_bucket_count,
                            gnu_hash_symbol_count,
                            |index| {
                                out_dynsyms[(gnu_hash_index_base + index) as usize]
                                    .gnu_hash
                                    .unwrap()
                            },
                        );
                    }
                    SectionData::GnuVersym => {
                        writer.write_null_gnu_versym();
                        for out_dynsym in &out_dynsyms {
                            let symbol = self.dynamic_symbols.get(out_dynsym.id);
                            let mut index = symbol.version.0 as u16;
                            if symbol.version_hidden {
                                index |= elf::VERSYM_HIDDEN;
                            }
                            writer.write_gnu_versym(index);
                        }
                    }
                    SectionData::GnuVerdef => {
                        writer.write_align_gnu_verdef();
                        if let Some(version_base) = &self.version_base {
                            let verdef = write::elf::Verdef {
                                version: elf::VER_DEF_CURRENT,
                                flags: elf::VER_FLG_BASE,
                                index: 1,
                                aux_count: 1,
                                name: writer.get_dynamic_string(version_base),
                            };
                            if verdef_shared_base {
                                writer.write_gnu_verdef_shared(&verdef);
                            } else {
                                writer.write_gnu_verdef(&verdef);
                            }
                        }
                        for version in &self.versions {
                            if let VersionData::Def(def) = &version.data {
                                let mut names = def.names.iter();
                                let name = names.next().ok_or_else(|| {
                                    Error(format!("Missing SHT_GNU_VERDEF name {}", version.id.0))
                                })?;
                                writer.write_gnu_verdef(&write::elf::Verdef {
                                    version: elf::VER_DEF_CURRENT,
                                    flags: def.flags,
                                    index: version.id.0 as u16,
                                    aux_count: def.names.len() as u16,
                                    name: writer.get_dynamic_string(name),
                                });
                                for name in names {
                                    writer.write_gnu_verdaux(writer.get_dynamic_string(name));
                                }
                            }
                        }
                    }
                    SectionData::GnuVerneed => {
                        writer.write_align_gnu_verneed();
                        for file in &self.version_files {
                            let out_file = &out_version_files[file.id.0];
                            if out_file.versions.is_empty() {
                                continue;
                            }
                            writer.write_gnu_verneed(&write::elf::Verneed {
                                version: elf::VER_NEED_CURRENT,
                                aux_count: out_file.versions.len() as u16,
                                file: writer.get_dynamic_string(&file.name),
                            });
                            for id in &out_file.versions {
                                let version = self.versions.get(*id);
                                // This will always match.
                                if let VersionData::Need(need) = &version.data {
                                    debug_assert_eq!(*id, version.id);
                                    writer.write_gnu_vernaux(&write::elf::Vernaux {
                                        flags: need.flags,
                                        index: version.id.0 as u16,
                                        name: writer.get_dynamic_string(&need.name),
                                    });
                                }
                            }
                        }
                    }
                    _ => {
                        return Err(Error(format!(
                            "Unsupported alloc section type {:x}",
                            section.sh_type
                        )));
--> --------------------

--> maximum size reached

--> --------------------

[ zur Elbe Produktseite wechseln0.56Quellennavigators  Analyse erneut starten  ]