//! 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;
/// 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)] pubstruct 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<()>,
}
#[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)
} elseif 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)
} elseif 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>,
{ letmut relocations = Vec::new(); for rel in rels { let rel = (*rel).into(); let symbol = iflet 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>,
{ letmut 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))
}
iflet Some((mut verdefs, link)) = sections.gnu_verdef(endian, data)? { if link != dynamic_symbols.string_section() { return Err(Error::new("Invalid SHT_GNU_VERDEF section"));
} whilelet 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"));
} ifself.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 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 { iflet SectionData::Dynamic(dynamics) = §ion.data { for dynamic in dynamics { iflet Dynamic::String { val, .. } = dynamic {
writer.add_dynamic_string(val);
}
}
}
}
// Assign dynamic symbol indices. letmut 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) { letmut name = None; letmut hash = None; letmut 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. letmut gnu_hash_symbol_count = 0; if gnu_hash_id.is_some() { ifself.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;
} letmut 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. letmut 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(); letmut 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. letmut verdef_count = 0; letmut verdaux_count = 0; letmut verdef_shared_base = false; letmut verneed_count = 0; letmut vernaux_count = 0; letmut out_version_files = vec![VersionFileOut::default(); self.version_files.len()]; iflet 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;
} letmut 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 { iflet 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 { iflet 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",
));
} elseif 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"));
} elseif 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"));
} elseif 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",
));
}
if !self.segments.is_empty() { // TODO: support program headers in other locations. ifself.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);
}
letmut 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
)));
}
};
}
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 = iflet 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 => { ifself.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 => { ifself.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); letmut index = symbol.version.0as u16; if symbol.version_hidden {
index |= elf::VERSYM_HIDDEN;
}
writer.write_gnu_versym(index);
}
}
SectionData::GnuVerdef => {
writer.write_align_gnu_verdef(); iflet 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 { iflet VersionData::Def(def) = &version.data { letmut 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.0as 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. iflet VersionData::Need(need) = &version.data {
debug_assert_eq!(*id, version.id);
writer.write_gnu_vernaux(&write::elf::Vernaux {
flags: need.flags,
index: version.id.0as 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 = iflet 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,
},
);
}
}
/// 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`. pubfn delete_orphans(&mutself) { 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. pubfn delete_orphan_segments(&mutself) { let sections = &self.sections; for segment in &mutself.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. pubfn delete_orphan_symbols(&mutself) { for symbol in &mutself.symbols { iflet Some(section) = symbol.section { ifself.sections.get_mut(section).delete {
symbol.delete = true;
}
}
} for symbol in &mutself.dynamic_symbols { iflet Some(section) = symbol.section { ifself.sections.get_mut(section).delete {
symbol.delete = true;
}
}
}
}
/// Delete relocations that refer to deleted symbols. pubfn delete_orphan_relocations(&mutself) { let symbols = &self.symbols; let dynamic_symbols = &self.dynamic_symbols; for section in &mutself.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 unused GNU version entries. pubfn delete_unused_versions(&mutself) { letmut version_used = vec![false; self.versions.len() + VERSION_ID_BASE]; for symbol in &self.dynamic_symbols {
version_used[symbol.version.0] = true;
} letmut version_file_used = vec![false; self.version_files.len()]; for version in &mutself.versions { if !version_used[version.id.0] {
version.delete = true; continue;
} iflet VersionData::Need(need) = &version.data {
version_file_used[need.file.0] = true;
}
} for file in &mutself.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. pubfn class(&self) -> write::elf::Class {
write::elf::Class { is_64: self.is_64 }
}
/// Calculate the size of the file header. pubfn file_header_size(&self) -> usize { self.class().file_header_size()
}
/// Calculate the size of the program headers. pubfn 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`]. pubfn 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`]. pubfn dynamic_string_size(&self) -> usize { letmut dynstr = write::string::StringTable::default(); for section in &self.sections { iflet SectionData::Dynamic(dynamics) = §ion.data { for dynamic in dynamics { iflet Dynamic::String { val, .. } = dynamic {
dynstr.add(val);
}
}
}
} for symbol in &self.dynamic_symbols {
dynstr.add(&symbol.name);
} iflet 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`]. pubfn 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`]. pubfn 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`]. pubfn 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`]. pubfn gnu_verdef_size(&self) -> usize { letmut verdef_count = 0; letmut verdaux_count = 0; ifself.version_base.is_some() {
verdef_count += 1;
verdaux_count += 1;
} for version in &self.versions { iflet 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`]. pubfn gnu_verneed_size(&self) -> usize { let verneed_count = self.version_files.count(); letmut vernaux_count = 0; for version in &self.versions { iflet 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`]. pubfn section_size(&self, section: &Section<'_>) -> usize { if section.delete || !section.is_alloc() { return0;
} 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`]. pubfn set_section_sizes(&mutself) { 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. pubfn 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, pubfn 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, pubfn dynamic_data_mut(&mutself) -> Option<&mut Vec<Dynamic<'data>>> { let section = self.dynamic_section()?; match &mutself.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. pubfn 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. pubfn 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. pubfn interp_data_mut(&mutself) -> Option<&mut Bytes<'data>> { let section = self.interp_section()?; match &mutself.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)] pubstruct 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)] pubstruct SegmentId(usize);
/// A segment in [`Segments`]. /// /// This corresponds to [`elf::ProgramHeader32`] or [`elf::ProgramHeader64`]. #[derive(Debug)] pubstruct 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. pubfn id(&self) -> SegmentId { self.id
}
/// Returns true if the segment type is `PT_LOAD`. pubfn is_load(&self) -> bool { self.p_type == elf::PT_LOAD
}
/// Returns true if the segment contains the given file offset. pubfn 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. pubfn 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. pubfn 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. pubfn remove_sections(&mutself) { 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. pubfn append_section(&mutself, section: &mut Section<'_>) {
debug_assert_eq!(self.p_filesz, self.p_memsz); ifself.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. pubfn append_section_range(&mutself, section: &Section<'_>) { let section_filesize = if section.sh_type == elf::SHT_NOBITS { 0
} else {
section.sh_size
}; ifself.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 { ifself.p_offset > section.sh_offset { self.p_offset = section.sh_offset;
} let filesz = section.sh_offset + section_filesize - self.p_offset; ifself.p_filesz < filesz { self.p_filesz = filesz;
} ifself.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; ifself.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. pubfn recalculate_ranges(&mutself, 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(&mutself.sections); for id in &ids { let section = sections.get(*id); self.append_section_range(section);
} self.sections = ids;
}
}
/// A segment table. pubtype Segments<'data> = Table<Segment<'data>>;
impl<'data> Segments<'data> { /// Add a new segment to the table. pubfn add(&mutself) -> &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. pubfn 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. pubfn add_load_segment(&mutself, flags: u32, align: u64) -> &tyle='color:red'>mut Segment<'data> { letmut max_offset = 0; letmut 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));
/// 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`. pubfn copy(&mutself, 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)] pubstruct SectionId(usize);
/// A section in [`Sections`]. /// /// This corresponds to [`elf::SectionHeader32`] or [`elf::SectionHeader64`]. #[derive(Debug)] pubstruct 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. pubfn id(&self) -> SectionId { self.id
}
/// Returns true if the section flags include `SHF_ALLOC`. pubfn 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. pubfn p_flags(&self) -> u32 { letmut p_flags = elf::PF_R; ifself.sh_flags & u64::from(elf::SHF_WRITE) != 0 {
p_flags |= elf::PF_W;
} ifself.sh_flags & u64::from(elf::SHF_EXECINSTR) != 0 {
p_flags |= elf::PF_X;
}
p_flags
}
}
/// The data for a [`Section`]. #[derive(Debug, Clone)] pubenum 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. pubtype Sections<'data> = Table<Section<'data>>;
impl<'data> Sections<'data> { /// Add a new section to the table. pubfn add(&mutself) -> &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. pubfn copy(&mutself, 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)] pubstruct SymbolId<const DYNAMIC: bool = false>(usize);
/// A symbol in [`Symbols`]. /// /// This corresponds to [`elf::Sym32`] or [`elf::Sym64`]. #[derive(Debug)] pubstruct 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. pubfn id(&self) -> SymbolId<DYNAMIC> { self.id
}
/// Get the `st_bind` component of the `st_info` field. #[inline] pubfn st_bind(&self) -> u8 { self.st_info >> 4
}
/// Get the `st_type` component of the `st_info` field. #[inline] pubfn st_type(&self) -> u8 { self.st_info & 0xf
}
/// Set the `st_info` field given the `st_bind` and `st_type` components. #[inline] pubfn set_st_info(&mutself, st_bind: u8, st_type: u8) { self.st_info = (st_bind << 4) + (st_type & 0xf);
}
}
/// A symbol table. pubtype Symbols<'data, const DYNAMIC: bool = false> = Table<Symbol<'data, DYNAMIC>>;
impl<'data, const DYNAMIC: bool> Symbols<'data, DYNAMIC> { /// Number of defined symbols. pubfn count_defined(&self) -> usize { self.into_iter()
.filter(|symbol| symbol.st_shndx != elf::SHN_UNDEF)
.count()
}
/// Add a new symbol to the table. pubfn add(&mutself) -> &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)] pubstruct 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. pubtype DynamicSymbolId = SymbolId<true>;
/// A dynamic symbol. pubtype DynamicSymbol<'data> = Symbol<'data, true>;
/// A dynamic symbol table. pubtype DynamicSymbols<'data> = Symbols<'data, true>;
/// A dynamic relocation. pubtype DynamicRelocation = Relocation<true>;
/// An entry in the dynamic section. /// /// This corresponds to [`elf::Dyn32`] or [`elf::Dyn64`]. #[derive(Debug, Clone, PartialEq, Eq)] pubenum 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. pubfn tag(&self) -> u32 { matchself {
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)] pubstruct VersionFileId(usize);
/// A filename used for GNU versioning. /// /// Stored in [`VersionFiles`]. #[derive(Debug)] pubstruct 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. pubfn id(&self) -> VersionFileId { self.id
}
}
/// A table of filenames used for GNU versioning. pubtype VersionFiles<'data> = Table<VersionFile<'data>>;
impl<'data> VersionFiles<'data> { /// Add a new filename to the table. pubfn add(&mutself, 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)] pubstruct VersionId(usize);
impl VersionId { /// Return `True` if this is a special version that does not exist in the version table. pubfn is_special(&self) -> bool { self.0 < VERSION_ID_BASE
}
/// Return the ID for a version index of [`elf::VER_NDX_LOCAL`]. pubfn local() -> Self {
VersionId(elf::VER_NDX_LOCAL as usize)
}
/// Return the ID for a version index of [`elf::VER_NDX_GLOBAL`]. pubfn global() -> Self {
VersionId(elf::VER_NDX_GLOBAL as usize)
}
}
/// A version for a symbol. #[derive(Debug)] pubstruct 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. pubfn id(&self) -> VersionId { self.id
}
}
/// The data for a version for a symbol. #[derive(Debug)] pubenum 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)] pubstruct 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)] pubstruct 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. pubtype Versions<'data> = Table<Version<'data>>;
impl<'data> Versions<'data> { /// Add a version. pubfn add(&mutself, 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)] pubstruct AttributesSection<'data> { /// The subsections. pub subsections: Vec<AttributesSubsection<'data>>,
}
impl<'data> AttributesSection<'data> { /// Create a new attributes section. pubfn new() -> Self { Self::default()
}
}
/// A subsection of an attributes section. #[derive(Debug, Clone)] pubstruct AttributesSubsection<'data> { /// The vendor namespace for these attributes. pub vendor: ByteString<'data>, /// The sub-subsections. pub subsubsections: Vec<AttributesSubsubsection<'data>>,
}
/// A sub-subsection in an attributes section. #[derive(Debug, Clone)] pubstruct 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)] pubenum 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. pubfn tag(&self) -> u8 { matchself {
AttributeTag::File => elf::Tag_File,
AttributeTag::Section(_) => elf::Tag_Section,
AttributeTag::Symbol(_) => elf::Tag_Symbol,
}
}
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.98Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.