Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


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

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

[ Dauer der Verarbeitung: 0.9 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge