diff options
Diffstat (limited to 'vendor/object/src/build')
| -rw-r--r-- | vendor/object/src/build/bytes.rs | 146 | ||||
| -rw-r--r-- | vendor/object/src/build/elf.rs | 3130 | ||||
| -rw-r--r-- | vendor/object/src/build/error.rs | 43 | ||||
| -rw-r--r-- | vendor/object/src/build/mod.rs | 18 | ||||
| -rw-r--r-- | vendor/object/src/build/table.rs | 128 |
5 files changed, 3465 insertions, 0 deletions
diff --git a/vendor/object/src/build/bytes.rs b/vendor/object/src/build/bytes.rs new file mode 100644 index 00000000..23635585 --- /dev/null +++ b/vendor/object/src/build/bytes.rs @@ -0,0 +1,146 @@ +use alloc::borrow::Cow; +use alloc::string::String; +use alloc::vec::Vec; +use core::fmt; + +/// A byte slice. +/// +/// Uses copy-on-write to avoid unnecessary allocations. The bytes can be +/// accessed as a slice using the `Deref` trait, or as a mutable `Vec` using the +/// `to_mut` method. +/// +/// Provides a `Debug` implementation that shows the first 8 bytes and the length. +#[derive(Default, Clone, PartialEq, Eq)] +pub struct Bytes<'a>(Cow<'a, [u8]>); + +impl<'a> Bytes<'a> { + /// Acquire a mutable reference to the bytes. + /// + /// Clones the bytes if they are shared. + pub fn to_mut(&mut self) -> &mut Vec<u8> { + self.0.to_mut() + } + + /// Get the bytes as a slice. + pub fn as_slice(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl<'a> core::ops::Deref for Bytes<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.deref() + } +} + +impl<'a> From<&'a [u8]> for Bytes<'a> { + fn from(bytes: &'a [u8]) -> Self { + Bytes(Cow::Borrowed(bytes)) + } +} + +impl<'a> From<Vec<u8>> for Bytes<'a> { + fn from(bytes: Vec<u8>) -> Self { + Bytes(Cow::Owned(bytes)) + } +} + +impl<'a> fmt::Debug for Bytes<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_list_bytes(&self.0, f) + } +} + +// Only for Debug impl of `Bytes`. +fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = fmt.debug_list(); + list.entries(bytes.iter().take(8).copied().map(DebugByte)); + if bytes.len() > 8 { + list.entry(&DebugLen(bytes.len())); + } + list.finish() +} + +struct DebugByte(u8); + +impl fmt::Debug for DebugByte { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "0x{:02x}", self.0) + } +} + +struct DebugLen(usize); + +impl fmt::Debug for DebugLen { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "...; {}", self.0) + } +} + +/// A byte slice that is a string of an unknown encoding. +/// +/// Uses copy-on-write to avoid unnecessary allocations. The bytes can be +/// accessed as a slice using the `Deref` trait, or as a mutable `Vec` using the +/// `to_mut` method. +/// +/// Provides a `Debug` implementation that interprets the bytes as UTF-8. +#[derive(Default, Clone, PartialEq, Eq, Hash)] +pub struct ByteString<'a>(Cow<'a, [u8]>); + +impl<'a> ByteString<'a> { + /// Acquire a mutable reference to the bytes. + /// + /// Clones the bytes if they are shared. + pub fn to_mut(&mut self) -> &mut Vec<u8> { + self.0.to_mut() + } + + /// Get the bytes as a slice. + pub fn as_slice(&self) -> &[u8] { + self.0.as_ref() + } +} + +impl<'a> core::borrow::Borrow<[u8]> for ByteString<'a> { + fn borrow(&self) -> &[u8] { + self.0.borrow() + } +} + +impl<'a> core::ops::Deref for ByteString<'a> { + type Target = [u8]; + fn deref(&self) -> &[u8] { + self.0.deref() + } +} + +impl<'a> From<&'a [u8]> for ByteString<'a> { + fn from(bytes: &'a [u8]) -> Self { + ByteString(Cow::Borrowed(bytes)) + } +} + +impl<'a> From<Vec<u8>> for ByteString<'a> { + fn from(bytes: Vec<u8>) -> Self { + ByteString(Cow::Owned(bytes)) + } +} + +impl<'a> From<&'a str> for ByteString<'a> { + fn from(s: &'a str) -> Self { + ByteString(Cow::Borrowed(s.as_bytes())) + } +} + +impl<'a> fmt::Debug for ByteString<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"{}\"", String::from_utf8_lossy(&self.0)) + } +} + +impl<'a> fmt::Display for ByteString<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "{}", String::from_utf8_lossy(&self.0)) + } +} diff --git a/vendor/object/src/build/elf.rs b/vendor/object/src/build/elf.rs new file mode 100644 index 00000000..a32deb3c --- /dev/null +++ b/vendor/object/src/build/elf.rs @@ -0,0 +1,3130 @@ +//! 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 name = sections.section_name(endian, section)?; + let data = match section.sh_type(endian) { + elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()), + // Section types that we treat as opaque data. In future, some of these could be + // changed to a parsed variant if we need to modify their contents. + elf::SHT_PROGBITS + | elf::SHT_INIT_ARRAY + | elf::SHT_FINI_ARRAY + | elf::SHT_PREINIT_ARRAY + | elf::SHT_RELR + | elf::SHT_LLVM_DEPENDENT_LIBRARIES => { + 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 if name == b".annobin.notes" { + // Not actually a string table because nothing references the strings. + // We simply need to preserve the data (similar to a .comment section). + SectionData::Data(section.data(endian, data)?.into()) + } 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: name.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 + ))); + } + } + } + } + + // Write non-alloc sections. + for out_section in &mut out_sections { + let section = self.sections.get(out_section.id); + if !self.segments.is_empty() && section.is_alloc() { + continue; + } + match §ion.data { + SectionData::Data(data) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(out_section.offset, writer.len()); + writer.write(data); + } + SectionData::UninitializedData(_) => { + // Nothing to do. + } + SectionData::Note(data) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(out_section.offset, writer.len()); + writer.write(data); + } + SectionData::Attributes(_) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(out_section.offset, writer.len()); + writer.write(&out_section.attributes); + } + // These are handled elsewhere. + SectionData::Relocation(_) + | SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String => {} + _ => { + return Err(Error(format!( + "Unsupported non-alloc section type {:x}", + section.sh_type + ))); + } + } + } + + writer.write_null_symbol(); + for out_sym in &out_syms { + let symbol = self.symbols.get(out_sym.id); + let section = symbol.section.map(|id| out_sections_index[id.0].unwrap()); + writer.write_symbol(&write::elf::Sym { + name: out_sym.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, + }); + } + writer.write_symtab_shndx(); + writer.write_strtab(); + + // Write non-alloc relocations. + for section in &self.sections { + if !self.segments.is_empty() && section.is_alloc() { + continue; + } + let SectionData::Relocation(relocations) = §ion.data else { + continue; + }; + writer.write_align_relocation(); + for rel in relocations { + let r_sym = if let Some(id) = rel.symbol { + out_syms_index[id.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, + }, + ); + } + } + + writer.write_shstrtab(); + + writer.write_null_section_header(); + for out_section in &out_sections { + let section = self.sections.get(out_section.id); + match §ion.data { + SectionData::Data(_) + | SectionData::UninitializedData(_) + | SectionData::Relocation(_) + | SectionData::DynamicRelocation(_) + | SectionData::Note(_) + | SectionData::Dynamic(_) + | SectionData::Attributes(_) => { + let sh_size = match §ion.data { + SectionData::Data(data) => data.len() as u64, + SectionData::UninitializedData(len) => *len, + SectionData::Relocation(relocations) => { + (relocations.len() + * self.class().rel_size(section.sh_type == elf::SHT_RELA)) + as u64 + } + SectionData::DynamicRelocation(relocations) => { + (relocations.len() + * self.class().rel_size(section.sh_type == elf::SHT_RELA)) + as u64 + } + SectionData::Note(data) => data.len() as u64, + SectionData::Dynamic(dynamics) => { + ((1 + dynamics.len()) * self.class().dyn_size()) as u64 + } + SectionData::Attributes(_) => out_section.attributes.len() as u64, + _ => { + return Err(Error(format!( + "Unimplemented size for section type {:x}", + section.sh_type + ))) + } + }; + let sh_link = if let Some(id) = section.sh_link_section { + if let Some(index) = out_sections_index[id.0] { + index.0 + } else { + return Err(Error(format!( + "Invalid sh_link from section '{}' to deleted section '{}'", + section.name, + self.sections.get(id).name, + ))); + } + } else { + 0 + }; + let sh_info = if let Some(id) = section.sh_info_section { + if let Some(index) = out_sections_index[id.0] { + index.0 + } else { + return Err(Error(format!( + "Invalid sh_info link from section '{}' to deleted section '{}'", + section.name, + self.sections.get(id).name, + ))); + } + } else { + section.sh_info + }; + writer.write_section_header(&write::elf::SectionHeader { + name: out_section.name, + sh_type: section.sh_type, + sh_flags: section.sh_flags, + sh_addr: section.sh_addr, + sh_offset: out_section.offset as u64, + sh_size, + sh_link, + sh_info, + sh_addralign: section.sh_addralign, + sh_entsize: section.sh_entsize, + }); + } + SectionData::SectionString => { + writer.write_shstrtab_section_header(); + } + SectionData::Symbol => { + writer.write_symtab_section_header(1 + num_local as u32); + } + SectionData::SymbolSectionIndex => { + writer.write_symtab_shndx_section_header(); + } + SectionData::String => { + writer.write_strtab_section_header(); + } + SectionData::DynamicString => { + writer.write_dynstr_section_header(section.sh_addr); + } + SectionData::DynamicSymbol => { + writer + .write_dynsym_section_header(section.sh_addr, 1 + num_local_dynamic as u32); + } + SectionData::Hash => { + writer.write_hash_section_header(section.sh_addr); + } + SectionData::GnuHash => { + writer.write_gnu_hash_section_header(section.sh_addr); + } + SectionData::GnuVersym => { + writer.write_gnu_versym_section_header(section.sh_addr); + } + SectionData::GnuVerdef => { + writer.write_gnu_verdef_section_header(section.sh_addr); + } + SectionData::GnuVerneed => { + writer.write_gnu_verneed_section_header(section.sh_addr); + } + } + } + debug_assert_eq!(writer.reserved_len(), writer.len()); + Ok(()) + } + + /// Delete segments, symbols, relocations, and dynamics that refer + /// to deleted items. + /// + /// This calls `delete_orphan_segments`, `delete_orphan_symbols`, + /// `delete_orphan_relocations`, and `delete_orphan_dynamics`. + pub fn delete_orphans(&mut self) { + self.delete_orphan_segments(); + self.delete_orphan_symbols(); + self.delete_orphan_relocations(); + self.delete_orphan_dynamics(); + } + + /// Set the delete flag for segments that only refer to deleted sections. + pub fn delete_orphan_segments(&mut self) { + let sections = &self.sections; + for segment in &mut self.segments { + // We only delete segments that have become empty due to section deletions. + if segment.sections.is_empty() { + continue; + } + segment.sections.retain(|id| !sections.get(*id).delete); + segment.delete = segment.sections.is_empty(); + } + } + + /// Set the delete flag for symbols that refer to deleted sections. + pub fn delete_orphan_symbols(&mut self) { + for symbol in &mut self.symbols { + if let Some(section) = symbol.section { + if self.sections.get_mut(section).delete { + symbol.delete = true; + } + } + } + for symbol in &mut self.dynamic_symbols { + if let Some(section) = symbol.section { + if self.sections.get_mut(section).delete { + symbol.delete = true; + } + } + } + } + + /// Delete relocations that refer to deleted symbols. + pub fn delete_orphan_relocations(&mut self) { + let symbols = &self.symbols; + let dynamic_symbols = &self.dynamic_symbols; + for section in &mut self.sections { + match &mut section.data { + SectionData::Relocation(relocations) => { + relocations.retain(|relocation| match relocation.symbol { + None => true, + Some(id) => !symbols.get(id).delete, + }); + } + SectionData::DynamicRelocation(relocations) => { + relocations.retain(|relocation| match relocation.symbol { + None => true, + Some(id) => !dynamic_symbols.get(id).delete, + }); + } + _ => {} + } + } + } + + /// Delete dynamic entries that refer to deleted sections. + pub fn delete_orphan_dynamics(&mut self) { + let mut have_dynsym = false; + let mut have_dynstr = false; + let mut have_hash = false; + let mut have_gnu_hash = false; + let mut have_versym = false; + let mut have_verdef = false; + let mut have_verneed = false; + for section in &self.sections { + match §ion.data { + SectionData::DynamicSymbol => have_dynsym = true, + SectionData::DynamicString => have_dynstr = true, + SectionData::Hash => have_hash = true, + SectionData::GnuHash => have_gnu_hash = true, + SectionData::GnuVersym => have_versym = true, + SectionData::GnuVerdef => have_verdef = true, + SectionData::GnuVerneed => have_verneed = true, + _ => {} + } + } + for section in &mut self.sections { + if let SectionData::Dynamic(dynamics) = &mut section.data { + dynamics.retain(|dynamic| match dynamic { + Dynamic::Auto { + tag: elf::DT_SYMTAB, + } => have_dynsym, + Dynamic::Auto { + tag: elf::DT_STRTAB, + } + | Dynamic::Auto { tag: elf::DT_STRSZ } => have_dynstr, + Dynamic::Auto { tag: elf::DT_HASH } => have_hash, + Dynamic::Auto { + tag: elf::DT_GNU_HASH, + } => have_gnu_hash, + Dynamic::Auto { + tag: elf::DT_VERSYM, + } => have_versym, + Dynamic::Auto { + tag: elf::DT_VERNEED, + } + | Dynamic::Auto { + tag: elf::DT_VERNEEDNUM, + } => have_verneed, + Dynamic::Auto { + tag: elf::DT_VERDEF, + } + | Dynamic::Auto { + tag: elf::DT_VERDEFNUM, + } => have_verdef, + _ => true, + }); + } + } + } + + /// Delete unused GNU version entries. + pub fn delete_unused_versions(&mut self) { + let mut version_used = vec![false; self.versions.len() + VERSION_ID_BASE]; + for symbol in &self.dynamic_symbols { + version_used[symbol.version.0] = true; + } + let mut version_file_used = vec![false; self.version_files.len()]; + for version in &mut self.versions { + if let VersionData::Need(need) = &version.data { + // This is a dummy version that is required if DT_RELR is used. + if need.name.as_slice() == b"GLIBC_ABI_DT_RELR" { + version_used[version.id.0] = true; + } + } + if !version_used[version.id.0] { + version.delete = true; + continue; + } + if let VersionData::Need(need) = &version.data { + version_file_used[need.file.0] = true; + } + } + for file in &mut self.version_files { + if !version_file_used[file.id.0] { + file.delete = true; + } + } + } + + /// Return the ELF file class that will be written. + /// + /// This can be useful for calculating sizes. + pub fn class(&self) -> write::elf::Class { + write::elf::Class { is_64: self.is_64 } + } + + /// Calculate the size of the file header. + pub fn file_header_size(&self) -> usize { + self.class().file_header_size() + } + + /// Calculate the size of the program headers. + pub fn program_headers_size(&self) -> usize { + self.segments.count() * self.class().program_header_size() + } + + /// Calculate the size of the dynamic symbol table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn dynamic_symbol_size(&self) -> usize { + (1 + self.dynamic_symbols.count()) * self.class().sym_size() + } + + /// Calculate the size of the dynamic string table. + /// + /// This adds all of the currently used dynamic strings to a string table, + /// calculates the size of the string table, and discards the string table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn dynamic_string_size(&self) -> usize { + let mut dynstr = write::string::StringTable::default(); + for section in &self.sections { + if let SectionData::Dynamic(dynamics) = §ion.data { + for dynamic in dynamics { + if let Dynamic::String { val, .. } = dynamic { + dynstr.add(val); + } + } + } + } + for symbol in &self.dynamic_symbols { + dynstr.add(&symbol.name); + } + if let Some(version_base) = &self.version_base { + dynstr.add(version_base); + } + for version in &self.versions { + match &version.data { + VersionData::Def(def) => { + for name in &def.names { + dynstr.add(name); + } + } + VersionData::Need(need) => { + dynstr.add(&need.name); + } + } + } + for file in &self.version_files { + dynstr.add(&file.name); + } + dynstr.size(1) + } + + /// Calculate the size of the hash table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn hash_size(&self) -> usize { + let chain_count = 1 + self.dynamic_symbols.count(); + self.class() + .hash_size(self.hash_bucket_count, chain_count as u32) + } + + /// Calculate the size of the GNU hash table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn gnu_hash_size(&self) -> usize { + let symbol_count = self.dynamic_symbols.count_defined(); + self.class().gnu_hash_size( + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + symbol_count as u32, + ) + } + + /// Calculate the size of the GNU symbol version section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_versym_size(&self) -> usize { + let symbol_count = 1 + self.dynamic_symbols.count(); + self.class().gnu_versym_size(symbol_count) + } + + /// Calculate the size of the GNU version definition section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_verdef_size(&self) -> usize { + let mut verdef_count = 0; + let mut verdaux_count = 0; + if self.version_base.is_some() { + verdef_count += 1; + verdaux_count += 1; + } + for version in &self.versions { + if let VersionData::Def(def) = &version.data { + if !def.is_shared(verdef_count, self.version_base.as_ref()) { + verdaux_count += def.names.len(); + } + verdef_count += 1; + } + } + self.class().gnu_verdef_size(verdef_count, verdaux_count) + } + + /// Calculate the size of the GNU version dependency section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_verneed_size(&self) -> usize { + let verneed_count = self.version_files.count(); + let mut vernaux_count = 0; + for version in &self.versions { + if let VersionData::Need(_) = &version.data { + vernaux_count += 1; + } + } + self.class().gnu_verneed_size(verneed_count, vernaux_count) + } + + /// Calculate the memory size of a section. + /// + /// Returns 0 for sections that are deleted or aren't allocated. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn section_size(&self, section: &Section<'_>) -> usize { + if section.delete || !section.is_alloc() { + return 0; + } + match §ion.data { + SectionData::Data(data) => data.len(), + SectionData::UninitializedData(len) => *len as usize, + SectionData::Relocation(relocations) => { + relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) + } + SectionData::DynamicRelocation(relocations) => { + relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) + } + SectionData::Note(data) => data.len(), + SectionData::Dynamic(dynamics) => (1 + dynamics.len()) * self.class().dyn_size(), + SectionData::DynamicString => self.dynamic_string_size(), + SectionData::DynamicSymbol => self.dynamic_symbol_size(), + SectionData::Hash => self.hash_size(), + SectionData::GnuHash => self.gnu_hash_size(), + SectionData::GnuVersym => self.gnu_versym_size(), + SectionData::GnuVerdef => self.gnu_verdef_size(), + SectionData::GnuVerneed => self.gnu_verneed_size(), + // None of these should be allocated. + SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String + | SectionData::Attributes(_) => 0, + } + } + + /// Set the `sh_size` field for every allocated section. + /// + /// This is useful to call prior to doing memory layout. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn set_section_sizes(&mut self) { + for id in (0..self.sections.len()).map(SectionId) { + let section = self.sections.get(id); + if section.delete || !section.is_alloc() { + continue; + } + self.sections.get_mut(id).sh_size = self.section_size(section) as u64; + } + } + + /// Find the section containing the dynamic table. + /// + /// This uses the `PT_DYNAMIC` program header to find the dynamic section. + pub fn dynamic_section(&self) -> Option<SectionId> { + let segment = self + .segments + .iter() + .find(|segment| segment.p_type == elf::PT_DYNAMIC)?; + // TODO: handle multiple sections in the segment? + segment.sections.iter().copied().next() + } + + /// Find the dynamic table entries. + /// + /// This uses the `PT_DYNAMIC` program header to find the dynamic section, + pub fn dynamic_data(&self) -> Option<&[Dynamic<'data>]> { + let section = self.dynamic_section()?; + match &self.sections.get(section).data { + SectionData::Dynamic(dynamics) => Some(dynamics), + _ => None, + } + } + + /// Find the dynamic table entries. + /// + /// This uses the `PT_DYNAMIC` program header to find the dynamic section, + pub fn dynamic_data_mut(&mut self) -> Option<&mut Vec<Dynamic<'data>>> { + let section = self.dynamic_section()?; + match &mut self.sections.get_mut(section).data { + SectionData::Dynamic(dynamics) => Some(dynamics), + _ => None, + } + } + + /// Find the section containing the interpreter path. + /// + /// This uses the `PT_INTERP` program header to find the interp section. + pub fn interp_section(&self) -> Option<SectionId> { + let segment = self + .segments + .iter() + .find(|segment| segment.p_type == elf::PT_INTERP)?; + // TODO: handle multiple sections in the segment? + segment.sections.iter().copied().next() + } + + /// Find the interpreter path. + /// + /// This uses the `PT_INTERP` program header to find the interp section. + pub fn interp_data(&self) -> Option<&[u8]> { + let section = self.interp_section()?; + match &self.sections.get(section).data { + SectionData::Data(data) => Some(data), + _ => None, + } + } + + /// Find the interpreter path. + /// + /// This uses the `PT_INTERP` program header to find the interp section. + pub fn interp_data_mut(&mut self) -> Option<&mut Bytes<'data>> { + let section = self.interp_section()?; + match &mut self.sections.get_mut(section).data { + SectionData::Data(data) => Some(data), + _ => None, + } + } +} + +/// ELF file header. +/// +/// This corresponds to fields in [`elf::FileHeader32`] or [`elf::FileHeader64`]. +/// This only contains the ELF file header fields that can be modified. +/// The other fields are automatically calculated. +#[derive(Debug, Default)] +pub struct Header { + /// The OS ABI field in the file header. + /// + /// One of the `ELFOSABI*` constants. + pub os_abi: u8, + /// The ABI version field in the file header. + /// + /// The meaning of this field depends on the `os_abi` value. + pub abi_version: u8, + /// The object file type in the file header. + /// + /// One of the `ET_*` constants. + pub e_type: u16, + /// The architecture in the file header. + /// + /// One of the `EM_*` constants. + pub e_machine: u16, + /// Entry point virtual address in the file header. + pub e_entry: u64, + /// The processor-specific flags in the file header. + /// + /// A combination of the `EF_*` constants. + pub e_flags: u32, + /// The file offset of the program header table. + /// + /// Writing will fail if the program header table cannot be placed at this offset. + pub e_phoff: u64, +} + +/// An ID for referring to a segment in [`Segments`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SegmentId(usize); + +impl fmt::Debug for SegmentId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SegmentId({})", self.0) + } +} + +impl Id for SegmentId { + fn index(&self) -> usize { + self.0 + } +} + +impl IdPrivate for SegmentId { + fn new(id: usize) -> Self { + SegmentId(id) + } +} + +/// A segment in [`Segments`]. +/// +/// This corresponds to [`elf::ProgramHeader32`] or [`elf::ProgramHeader64`]. +#[derive(Debug)] +pub struct Segment<'data> { + id: SegmentId, + /// Ignore this segment when writing the ELF file. + pub delete: bool, + /// The `p_type` field in the ELF program header. + /// + /// One of the `PT_*` constants. + pub p_type: u32, + /// The `p_flags` field in the ELF program header. + /// + /// A combination of the `PF_*` constants. + pub p_flags: u32, + /// The `p_offset` field in the ELF program header. + /// + /// This is the file offset of the data in the segment. This should + /// correspond to the file offset of the sections that are placed in + /// this segment. Currently there is no support for section data + /// that is not contained in sections. + pub p_offset: u64, + /// The `p_vaddr` field in the ELF program header. + pub p_vaddr: u64, + /// The `p_paddr` field in the ELF program header. + pub p_paddr: u64, + /// The `p_filesz` field in the ELF program header. + pub p_filesz: u64, + /// The `p_memsz` field in the ELF program header. + pub p_memsz: u64, + /// The `p_align` field in the ELF program header. + pub p_align: u64, + /// The sections contained in this segment. + pub sections: Vec<SectionId>, + // Might need to add reference to data if no sections. + marker: PhantomData<&'data ()>, +} + +impl<'data> Item for Segment<'data> { + type Id = SegmentId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> Segment<'data> { + /// The ID used for referring to this segment. + pub fn id(&self) -> SegmentId { + self.id + } + + /// Returns true if the segment type is `PT_LOAD`. + pub fn is_load(&self) -> bool { + self.p_type == elf::PT_LOAD + } + + /// Returns true if the segment contains the given file offset. + pub fn contains_offset(&self, offset: u64) -> bool { + offset >= self.p_offset && offset - self.p_offset < self.p_filesz + } + + /// Return the address corresponding to the given file offset. + /// + /// This will return a meaningless value if `contains_offset` is false. + pub fn address_from_offset(&self, offset: u64) -> u64 { + self.p_vaddr + .wrapping_add(offset.wrapping_sub(self.p_offset)) + } + + /// Returns true if the segment contains the given address. + pub fn contains_address(&self, address: u64) -> bool { + address >= self.p_vaddr && address - self.p_vaddr < self.p_memsz + } + + /// Remove all sections from the segment, and set its size to zero. + pub fn remove_sections(&mut self) { + self.p_filesz = 0; + self.p_memsz = 0; + self.sections.clear(); + } + + /// Add a section to the segment. + /// + /// If this is a [`elf::PT_LOAD`] segment, then the file offset and address of the + /// section is changed to be at the end of the segment. + /// + /// The segment's file and address ranges are extended to include the section. + /// This uses the `sh_size` field of the section, not the size of the section data. + /// + /// The section's id is added to the segment's list of sections. + pub fn append_section(&mut self, section: &mut Section<'_>) { + debug_assert_eq!(self.p_filesz, self.p_memsz); + if self.p_type == elf::PT_LOAD { + let align = section.sh_addralign; + let offset = (self.p_offset + self.p_filesz + (align - 1)) & !(align - 1); + let addr = (self.p_paddr + self.p_memsz + (align - 1)) & !(align - 1); + section.sh_offset = offset; + section.sh_addr = addr; + } + self.append_section_range(section); + self.sections.push(section.id); + } + + /// Extend this segment's file and address ranges to include the given section. + /// + /// If the segment's `p_memsz` is zero, then this signifies that the segment + /// has no file or address range yet. In this case, the segment's file and address + /// ranges are set equal to the section. Otherwise, the segment's file and address + /// ranges are extended to include the section. + /// + /// This uses the `sh_size` field of the section, not the size of the section data. + pub fn append_section_range(&mut self, section: &Section<'_>) { + let section_filesize = if section.sh_type == elf::SHT_NOBITS { + 0 + } else { + section.sh_size + }; + if self.p_memsz == 0 { + self.p_offset = section.sh_offset; + self.p_filesz = section_filesize; + self.p_vaddr = section.sh_addr; + self.p_paddr = section.sh_addr; + self.p_memsz = section.sh_size; + } else { + if self.p_offset > section.sh_offset { + self.p_offset = section.sh_offset; + } + let filesz = section.sh_offset + section_filesize - self.p_offset; + if self.p_filesz < filesz { + self.p_filesz = filesz; + } + if self.p_vaddr > section.sh_addr { + self.p_vaddr = section.sh_addr; + self.p_paddr = section.sh_addr; + } + let memsz = section.sh_addr + section.sh_size - self.p_vaddr; + if self.p_memsz < memsz { + self.p_memsz = memsz; + } + } + } + + /// Recalculate the file and address ranges of the segment. + /// + /// Resets the segment's file and address ranges to zero, and then + /// calls `append_section_range` for each section in the segment. + pub fn recalculate_ranges(&mut self, sections: &Sections<'data>) { + self.p_offset = 0; + self.p_filesz = 0; + self.p_vaddr = 0; + self.p_paddr = 0; + self.p_memsz = 0; + let ids = core::mem::take(&mut self.sections); + for id in &ids { + let section = sections.get(*id); + self.append_section_range(section); + } + self.sections = ids; + } +} + +/// A segment table. +pub type Segments<'data> = Table<Segment<'data>>; + +impl<'data> Segments<'data> { + /// Add a new segment to the table. + pub fn add(&mut self) -> &mut Segment<'data> { + let id = self.next_id(); + self.push(Segment { + id, + delete: false, + p_type: 0, + p_flags: 0, + p_offset: 0, + p_vaddr: 0, + p_paddr: 0, + p_filesz: 0, + p_memsz: 0, + p_align: 0, + sections: Vec::new(), + marker: PhantomData, + }); + self.get_mut(id) + } + + /// Find a `PT_LOAD` segment containing the given offset. + pub fn find_load_segment_from_offset(&self, offset: u64) -> Option<&Segment<'data>> { + self.iter() + .find(|segment| segment.is_load() && segment.contains_offset(offset)) + } + + /// Add a new `PT_LOAD` segment to the table. + /// + /// The file offset and address will be derived from the current maximum for any segment. + /// The address will be chosen so that `p_paddr % align == p_offset % align`. + /// You may wish to use [`Builder::load_align`] for the alignment. + pub fn add_load_segment(&mut self, flags: u32, align: u64) -> &mut Segment<'data> { + let mut max_offset = 0; + let mut max_addr = 0; + for segment in &*self { + let offset = segment.p_offset + segment.p_filesz; + if max_offset < offset { + max_offset = offset; + } + let addr = segment.p_vaddr + segment.p_memsz; + if max_addr < addr { + max_addr = addr; + } + } + // No alignment is required for the segment file offset because sections + // will add their alignment to the file offset when they are added. + let offset = max_offset; + // The address must be chosen so that addr % align == offset % align. + let addr = ((max_addr + (align - 1)) & !(align - 1)) + (offset & (align - 1)); + + let segment = self.add(); + segment.p_type = elf::PT_LOAD; + segment.p_flags = flags; + segment.p_offset = offset; + segment.p_vaddr = addr; + segment.p_paddr = addr; + segment.p_align = align; + segment + } + + /// Add a copy of a segment to the table. + /// + /// This will copy the segment type, flags and alignment. + /// + /// Additionally, if the segment type is `PT_LOAD`, then the file offset and address + /// will be set as in `add_load_segment`. + pub fn copy(&mut self, id: SegmentId) -> &mut Segment<'data> { + let segment = self.get(id); + let p_type = segment.p_type; + let p_flags = segment.p_flags; + let p_align = segment.p_align; + if p_type == elf::PT_LOAD { + self.add_load_segment(p_flags, p_align) + } else { + let segment = self.add(); + segment.p_type = p_type; + segment.p_flags = p_flags; + segment.p_align = p_align; + segment + } + } +} + +/// An ID for referring to a section in [`Sections`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SectionId(usize); + +impl fmt::Debug for SectionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "SectionId({})", self.0) + } +} + +impl Id for SectionId { + fn index(&self) -> usize { + self.0 + } +} + +impl IdPrivate for SectionId { + fn new(id: usize) -> Self { + SectionId(id) + } +} + +/// A section in [`Sections`]. +/// +/// This corresponds to [`elf::SectionHeader32`] or [`elf::SectionHeader64`]. +#[derive(Debug)] +pub struct Section<'data> { + id: SectionId, + /// Ignore this section when writing the ELF file. + pub delete: bool, + /// The name of the section. + /// + /// This is automatically added to the section header string table, + /// and the resulting string table offset is used to set the `sh_name` + /// field in the ELF section header. + pub name: ByteString<'data>, + /// The `sh_type` field in the ELF section header. + /// + /// One of the `SHT_*` constants. + pub sh_type: u32, + /// The `sh_flags` field in the ELF section header. + /// + /// A combination of the `SHF_*` constants. + pub sh_flags: u64, + /// The `sh_addr` field in the ELF section header. + pub sh_addr: u64, + /// The `sh_offset` field in the ELF section header. + /// + /// This is the file offset of the data in the section. + /// Writing will fail if the data cannot be placed at this offset. + /// + /// This is only used for sections that have `SHF_ALLOC` set. + /// For other sections, the section data is written at the next available + /// offset. + pub sh_offset: u64, + /// The `sh_size` field in the ELF section header. + /// + /// This size is not used when writing. The size of the `data` field is + /// used instead. + pub sh_size: u64, + /// The ID of the section linked to by the `sh_link` field in the ELF section header. + pub sh_link_section: Option<SectionId>, + /// The `sh_info` field in the ELF section header. + /// + /// Only used if `sh_info_section` is `None`. + pub sh_info: u32, + /// The ID of the section linked to by the `sh_info` field in the ELF section header. + pub sh_info_section: Option<SectionId>, + /// The `sh_addralign` field in the ELF section header. + pub sh_addralign: u64, + /// The `sh_entsize` field in the ELF section header. + pub sh_entsize: u64, + /// The section data. + pub data: SectionData<'data>, +} + +impl<'data> Item for Section<'data> { + type Id = SectionId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> Section<'data> { + /// The ID used for referring to this section. + pub fn id(&self) -> SectionId { + self.id + } + + /// Returns true if the section flags include `SHF_ALLOC`. + pub fn is_alloc(&self) -> bool { + self.sh_flags & u64::from(elf::SHF_ALLOC) != 0 + } + + /// Return the segment permission flags that are equivalent to the section flags. + pub fn p_flags(&self) -> u32 { + let mut p_flags = elf::PF_R; + if self.sh_flags & u64::from(elf::SHF_WRITE) != 0 { + p_flags |= elf::PF_W; + } + if self.sh_flags & u64::from(elf::SHF_EXECINSTR) != 0 { + p_flags |= elf::PF_X; + } + p_flags + } +} + +/// The data for a [`Section`]. +#[derive(Debug, Clone)] +pub enum SectionData<'data> { + /// The section contains the given raw data bytes. + Data(Bytes<'data>), + /// The section contains uninitialised data bytes of the given length. + UninitializedData(u64), + /// The section contains relocations. + Relocation(Vec<Relocation>), + /// The section contains dynamic relocations. + DynamicRelocation(Vec<DynamicRelocation>), + /// The section contains notes. + // TODO: parse notes + Note(Bytes<'data>), + /// The section contains dynamic entries. + Dynamic(Vec<Dynamic<'data>>), + /// The section contains attributes. + /// + /// This may be GNU attributes or other vendor-specific attributes. + Attributes(AttributesSection<'data>), + /// The section contains the strings for the section headers. + SectionString, + /// The section contains the symbol table. + Symbol, + /// The section contains the extended section index for the symbol table. + SymbolSectionIndex, + /// The section contains the strings for symbol table. + String, + /// The section contains the dynamic symbol table. + DynamicSymbol, + /// The section contains the dynamic string table. + DynamicString, + /// The section contains the hash table. + Hash, + /// The section contains the GNU hash table. + GnuHash, + /// The section contains the GNU symbol versions. + GnuVersym, + /// The section contains the GNU version definitions. + GnuVerdef, + /// The section contains the GNU version dependencies. + GnuVerneed, +} + +/// A section table. +pub type Sections<'data> = Table<Section<'data>>; + +impl<'data> Sections<'data> { + /// Add a new section to the table. + pub fn add(&mut self) -> &mut Section<'data> { + let id = self.next_id(); + self.push(Section { + id, + delete: false, + name: ByteString::default(), + sh_type: 0, + sh_flags: 0, + sh_addr: 0, + sh_offset: 0, + sh_size: 0, + sh_link_section: None, + sh_info: 0, + sh_info_section: None, + sh_addralign: 0, + sh_entsize: 0, + data: SectionData::Data(Bytes::default()), + }) + } + + /// Add a copy of a section to the table. + /// + /// This will set the file offset of the copy to zero. + /// [`Segment::append_section`] can be used to assign a valid file offset and a new address. + pub fn copy(&mut self, id: SectionId) -> &mut Section<'data> { + let section = self.get(id); + let id = self.next_id(); + let name = section.name.clone(); + let sh_type = section.sh_type; + let sh_flags = section.sh_flags; + let sh_addr = section.sh_addr; + let sh_size = section.sh_size; + let sh_link_section = section.sh_link_section; + let sh_info = section.sh_info; + let sh_info_section = section.sh_info_section; + let sh_addralign = section.sh_addralign; + let sh_entsize = section.sh_entsize; + let data = section.data.clone(); + self.push(Section { + id, + delete: false, + name, + sh_type, + sh_flags, + sh_addr, + sh_offset: 0, + sh_size, + sh_link_section, + sh_info, + sh_info_section, + sh_addralign, + sh_entsize, + data, + }) + } +} + +/// An ID for referring to a symbol in [`Symbols`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct SymbolId<const DYNAMIC: bool = false>(usize); + +impl<const DYNAMIC: bool> fmt::Debug for SymbolId<DYNAMIC> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl<const DYNAMIC: bool> Id for SymbolId<DYNAMIC> { + fn index(&self) -> usize { + self.0 + } +} + +impl<const DYNAMIC: bool> IdPrivate for SymbolId<DYNAMIC> { + fn new(id: usize) -> Self { + SymbolId(id) + } +} + +/// A symbol in [`Symbols`]. +/// +/// This corresponds to [`elf::Sym32`] or [`elf::Sym64`]. +#[derive(Debug)] +pub struct Symbol<'data, const DYNAMIC: bool = false> { + id: SymbolId<DYNAMIC>, + /// Ignore this symbol when writing the ELF file. + pub delete: bool, + /// The name of the symbol. + pub name: ByteString<'data>, + /// The section referenced by the symbol. + /// + /// Used to set the `st_shndx` field in the ELF symbol. + pub section: Option<SectionId>, + /// The `st_info` field in the ELF symbol. + pub st_info: u8, + /// The `st_other` field in the ELF symbol. + pub st_other: u8, + /// The `st_shndx` field in the ELF symbol. + /// + /// Only used if `Self::section` is `None`. + pub st_shndx: u16, + /// The `st_value` field in the ELF symbol. + pub st_value: u64, + /// The `st_size` field in the ELF symbol. + pub st_size: u64, + /// GNU version for dynamic symbols. + pub version: VersionId, + /// Set the [`elf::VERSYM_HIDDEN`] flag for this symbol. + pub version_hidden: bool, +} + +impl<'data, const DYNAMIC: bool> Item for Symbol<'data, DYNAMIC> { + type Id = SymbolId<DYNAMIC>; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data, const DYNAMIC: bool> Symbol<'data, DYNAMIC> { + /// The ID used for referring to this symbol. + pub fn id(&self) -> SymbolId<DYNAMIC> { + self.id + } + + /// Get the `st_bind` component of the `st_info` field. + #[inline] + pub fn st_bind(&self) -> u8 { + self.st_info >> 4 + } + + /// Get the `st_type` component of the `st_info` field. + #[inline] + pub fn st_type(&self) -> u8 { + self.st_info & 0xf + } + + /// Set the `st_info` field given the `st_bind` and `st_type` components. + #[inline] + pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { + self.st_info = (st_bind << 4) + (st_type & 0xf); + } +} + +/// A symbol table. +pub type Symbols<'data, const DYNAMIC: bool = false> = Table<Symbol<'data, DYNAMIC>>; + +impl<'data, const DYNAMIC: bool> Symbols<'data, DYNAMIC> { + /// Number of defined symbols. + pub fn count_defined(&self) -> usize { + self.into_iter() + .filter(|symbol| symbol.st_shndx != elf::SHN_UNDEF) + .count() + } + + /// Add a new symbol to the table. + pub fn add(&mut self) -> &mut Symbol<'data, DYNAMIC> { + let id = self.next_id(); + self.push(Symbol { + id, + delete: false, + name: ByteString::default(), + section: None, + st_info: 0, + st_other: 0, + st_shndx: 0, + st_value: 0, + st_size: 0, + version: VersionId::local(), + version_hidden: false, + }) + } +} + +/// A relocation stored in a [`Section`]. +/// +/// This corresponds to [`elf::Rel32`], [`elf::Rela32`], [`elf::Rel64`] or [`elf::Rela64`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Relocation<const DYNAMIC: bool = false> { + /// The `r_offset` field in the ELF relocation. + pub r_offset: u64, + /// The symbol referenced by the ELF relocation. + pub symbol: Option<SymbolId<DYNAMIC>>, + /// The `r_type` field in the ELF relocation. + pub r_type: u32, + /// The `r_addend` field in the ELF relocation. + /// + /// Only used if the section type is `SHT_RELA`. + pub r_addend: i64, +} + +/// A dynamic symbol ID. +pub type DynamicSymbolId = SymbolId<true>; + +/// A dynamic symbol. +pub type DynamicSymbol<'data> = Symbol<'data, true>; + +/// A dynamic symbol table. +pub type DynamicSymbols<'data> = Symbols<'data, true>; + +/// A dynamic relocation. +pub type DynamicRelocation = Relocation<true>; + +/// An entry in the dynamic section. +/// +/// This corresponds to [`elf::Dyn32`] or [`elf::Dyn64`]. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Dynamic<'data> { + /// The value is an automatically generated integer. + /// + /// Writing will fail if the value cannot be automatically generated. + Auto { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + }, + /// The value is an integer. + Integer { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + /// The `d_val` field in the dynamic entry. + val: u64, + }, + /// The value is a string. + String { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + /// The string value. + /// + /// This will be stored in the dynamic string section. + val: ByteString<'data>, + }, +} + +impl<'data> Dynamic<'data> { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + pub fn tag(&self) -> u32 { + match self { + Dynamic::Auto { tag } => *tag, + Dynamic::Integer { tag, .. } => *tag, + Dynamic::String { tag, .. } => *tag, + } + } +} + +/// An ID for referring to a filename in [`VersionFiles`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct VersionFileId(usize); + +impl fmt::Debug for VersionFileId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VersionFileId({})", self.0) + } +} + +impl Id for VersionFileId { + fn index(&self) -> usize { + self.0 + } +} + +impl IdPrivate for VersionFileId { + fn new(id: usize) -> Self { + VersionFileId(id) + } +} + +/// A filename used for GNU versioning. +/// +/// Stored in [`VersionFiles`]. +#[derive(Debug)] +pub struct VersionFile<'data> { + id: VersionFileId, + /// Ignore this file when writing the ELF file. + pub delete: bool, + /// The filename. + pub name: ByteString<'data>, +} + +impl<'data> Item for VersionFile<'data> { + type Id = VersionFileId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> VersionFile<'data> { + /// The ID used for referring to this filename. + pub fn id(&self) -> VersionFileId { + self.id + } +} + +/// A table of filenames used for GNU versioning. +pub type VersionFiles<'data> = Table<VersionFile<'data>>; + +impl<'data> VersionFiles<'data> { + /// Add a new filename to the table. + pub fn add(&mut self, name: ByteString<'data>) -> VersionFileId { + let id = self.next_id(); + self.push(VersionFile { + id, + name, + delete: false, + }); + id + } +} + +const VERSION_ID_BASE: usize = 2; + +/// An ID for referring to a version in [`Versions`]. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct VersionId(usize); + +impl fmt::Debug for VersionId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "VersionId({})", self.0) + } +} + +impl Id for VersionId { + fn index(&self) -> usize { + self.0 - VERSION_ID_BASE + } +} + +impl IdPrivate for VersionId { + fn new(id: usize) -> Self { + VersionId(VERSION_ID_BASE + id) + } +} + +impl VersionId { + /// Return `True` if this is a special version that does not exist in the version table. + pub fn is_special(&self) -> bool { + self.0 < VERSION_ID_BASE + } + + /// Return the ID for a version index of [`elf::VER_NDX_LOCAL`]. + pub fn local() -> Self { + VersionId(elf::VER_NDX_LOCAL as usize) + } + + /// Return the ID for a version index of [`elf::VER_NDX_GLOBAL`]. + pub fn global() -> Self { + VersionId(elf::VER_NDX_GLOBAL as usize) + } +} + +/// A version for a symbol. +#[derive(Debug)] +pub struct Version<'data> { + id: VersionId, + /// The data for this version. + pub data: VersionData<'data>, + /// Ignore this version when writing the ELF file. + pub delete: bool, +} + +impl<'data> Item for Version<'data> { + type Id = VersionId; + + fn is_deleted(&self) -> bool { + self.delete + } +} + +impl<'data> Version<'data> { + /// The ID used for referring to this version. + pub fn id(&self) -> VersionId { + self.id + } +} + +/// The data for a version for a symbol. +#[derive(Debug)] +pub enum VersionData<'data> { + /// The version for a defined symbol. + Def(VersionDef<'data>), + /// The version for an undefined symbol. + Need(VersionNeed<'data>), +} + +/// A GNU version definition. +#[derive(Debug)] +pub struct VersionDef<'data> { + /// The names for the version. + /// + /// This usually has two elements. The first element is the name of this + /// version, and the second element is the name of the previous version + /// in the tree of versions. + pub names: Vec<ByteString<'data>>, + /// The version flags. + /// + /// A combination of the `VER_FLG_*` constants. + pub flags: u16, +} + +impl<'data> VersionDef<'data> { + /// Optimise for the common case where the first version is the same as the base version. + fn is_shared(&self, index: usize, base: Option<&ByteString<'_>>) -> bool { + index == 1 && self.names.len() == 1 && self.names.first() == base + } +} + +/// A GNU version dependency. +#[derive(Debug)] +pub struct VersionNeed<'data> { + /// The filename of the library providing this version. + pub file: VersionFileId, + /// The name of the version. + pub name: ByteString<'data>, + /// The version flags. + /// + /// A combination of the `VER_FLG_*` constants. + pub flags: u16, +} + +/// A table of versions that are referenced by symbols. +pub type Versions<'data> = Table<Version<'data>>; + +impl<'data> Versions<'data> { + /// Add a version. + pub fn add(&mut self, data: VersionData<'data>) -> VersionId { + let id = self.next_id(); + self.push(Version { + id, + data, + delete: false, + }); + id + } +} + +/// The contents of an attributes section. +#[derive(Debug, Default, Clone)] +pub struct AttributesSection<'data> { + /// The subsections. + pub subsections: Vec<AttributesSubsection<'data>>, +} + +impl<'data> AttributesSection<'data> { + /// Create a new attributes section. + pub fn new() -> Self { + Self::default() + } +} + +/// A subsection of an attributes section. +#[derive(Debug, Clone)] +pub struct AttributesSubsection<'data> { + /// The vendor namespace for these attributes. + pub vendor: ByteString<'data>, + /// The sub-subsections. + pub subsubsections: Vec<AttributesSubsubsection<'data>>, +} + +impl<'data> AttributesSubsection<'data> { + /// Create a new subsection. + pub fn new(vendor: ByteString<'data>) -> Self { + AttributesSubsection { + vendor, + subsubsections: Vec::new(), + } + } +} + +/// A sub-subsection in an attributes section. +#[derive(Debug, Clone)] +pub struct AttributesSubsubsection<'data> { + /// The sub-subsection tag. + pub tag: AttributeTag, + /// The data containing the attributes. + pub data: Bytes<'data>, +} + +/// The tag for a sub-subsection in an attributes section. +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AttributeTag { + /// The attributes apply to the whole file. + /// + /// Correspeonds to [`elf::Tag_File`]. + File, + /// The attributes apply to the given sections. + /// + /// Correspeonds to [`elf::Tag_Section`]. + Section(Vec<SectionId>), + /// The attributes apply to the given symbols. + /// + /// Correspeonds to [`elf::Tag_Symbol`]. + Symbol(Vec<SymbolId>), +} + +impl AttributeTag { + /// Return the corresponding `elf::Tag_*` value for this tag. + pub fn tag(&self) -> u8 { + match self { + AttributeTag::File => elf::Tag_File, + AttributeTag::Section(_) => elf::Tag_Section, + AttributeTag::Symbol(_) => elf::Tag_Symbol, + } + } +} diff --git a/vendor/object/src/build/error.rs b/vendor/object/src/build/error.rs new file mode 100644 index 00000000..1e56780e --- /dev/null +++ b/vendor/object/src/build/error.rs @@ -0,0 +1,43 @@ +use alloc::string::String; +use core::{fmt, result}; +#[cfg(feature = "std")] +use std::error; + +use crate::{read, write}; + +/// The error type used within the build module. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error(pub(super) String); + +impl Error { + pub(super) fn new(message: impl Into<String>) -> Self { + Error(message.into()) + } +} + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +#[cfg(feature = "std")] +impl error::Error for Error {} +#[cfg(all(not(feature = "std"), core_error))] +impl core::error::Error for Error {} + +impl From<read::Error> for Error { + fn from(error: read::Error) -> Error { + Error(format!("{}", error)) + } +} + +impl From<write::Error> for Error { + fn from(error: write::Error) -> Error { + Error(error.0) + } +} + +/// The result type used within the build module. +pub type Result<T> = result::Result<T, Error>; diff --git a/vendor/object/src/build/mod.rs b/vendor/object/src/build/mod.rs new file mode 100644 index 00000000..5945f86b --- /dev/null +++ b/vendor/object/src/build/mod.rs @@ -0,0 +1,18 @@ +//! Interface for building object files. +//! +//! This module provides common types and traits used in the builders. +//! +//! The submodules define the builders for each file format. + +mod error; +pub use error::{Error, Result}; + +mod bytes; +pub use bytes::{ByteString, Bytes}; + +mod table; +use table::IdPrivate; +pub use table::{Id, Item, Table}; + +#[cfg(feature = "elf")] +pub mod elf; diff --git a/vendor/object/src/build/table.rs b/vendor/object/src/build/table.rs new file mode 100644 index 00000000..63f276a5 --- /dev/null +++ b/vendor/object/src/build/table.rs @@ -0,0 +1,128 @@ +use alloc::vec::Vec; + +/// An item in a [`Table`]. +pub trait Item { + /// The type of identifier for the item. + type Id: Id; + + /// Return `True` if the item is deleted. + fn is_deleted(&self) -> bool; +} + +/// An identifier for referring to an item in a [`Table`]. +pub trait Id: IdPrivate { + /// Return the index of the item in the table. + fn index(&self) -> usize; +} + +mod id_private { + pub trait IdPrivate { + fn new(id: usize) -> Self; + } +} +pub(super) use id_private::IdPrivate; + +/// A table of items. +/// +/// Each item has a unique identifier. +/// Items can be deleted without changing the identifiers of other items. +#[derive(Debug)] +pub struct Table<T>(Vec<T>); + +impl<T> Table<T> { + pub(super) fn new() -> Self { + Table(Vec::new()) + } +} + +impl<T: Item> Table<T> { + pub(super) fn next_id(&self) -> T::Id { + T::Id::new(self.0.len()) + } + + pub(super) fn push(&mut self, item: T) -> &mut T { + self.0.push(item); + self.0.last_mut().unwrap() + } + + /// Number of items, including deleted items. + pub(super) fn len(&self) -> usize { + self.0.len() + } + + /// Return `True` if there are no non-deleted items. + pub fn is_empty(&self) -> bool { + self.into_iter().next().is_none() + } + + /// Number of non-deleted items. + pub fn count(&self) -> usize { + self.into_iter().count() + } + + /// Return a reference to an item. + pub fn get(&self, id: T::Id) -> &T { + self.0.get(id.index()).unwrap() + } + + /// Return a mutable reference to a segment. + pub fn get_mut(&mut self, id: T::Id) -> &mut T { + self.0.get_mut(id.index()).unwrap() + } + + /// Return an iterator for the segments. + pub fn iter(&self) -> TableIter<'_, T> { + self.into_iter() + } + + /// Return a mutable iterator for the segments. + pub fn iter_mut(&mut self) -> TableIterMut<'_, T> { + self.into_iter() + } +} + +impl<'a, T: Item> IntoIterator for &'a Table<T> { + type Item = &'a T; + type IntoIter = TableIter<'a, T>; + fn into_iter(self) -> TableIter<'a, T> { + TableIter { + iter: self.0.iter(), + } + } +} + +impl<'a, T: Item> IntoIterator for &'a mut Table<T> { + type Item = &'a mut T; + type IntoIter = TableIterMut<'a, T>; + fn into_iter(self) -> TableIterMut<'a, T> { + TableIterMut { + iter: self.0.iter_mut(), + } + } +} + +/// An iterator for non-deleted items in a [`Table`]. +#[derive(Debug)] +pub struct TableIter<'a, T> { + iter: core::slice::Iter<'a, T>, +} + +impl<'a, T: Item> Iterator for TableIter<'a, T> { + type Item = &'a T; + fn next(&mut self) -> Option<&'a T> { + self.iter.find(|item| !item.is_deleted()) + } +} + +/// An iterator for non-deleted items in a [`Table`]. +#[derive(Debug)] +pub struct TableIterMut<'a, T> { + iter: core::slice::IterMut<'a, T>, +} + +impl<'a, T: Item> Iterator for TableIterMut<'a, T> { + type Item = &'a mut T; + fn next(&mut self) -> Option<&'a mut T> { + self.iter.find(|item| !item.is_deleted()) + } +} |
