write/elf: add Encoder (#910)

This is the struct writing component of `Writer`, without any state management.
`Encoder` also includes size methods, replacing the need for `Class`.

`build::elf::Builder` already does a lot of its own state management, so
change it to use `Encoder`.

Breaking change: the structs for writing need more information computed
by the caller (string offsets and version string hashes).
Added `Writer` methods to return string offsets.
This commit is contained in:
Philip Craig
2026-05-31 16:47:58 +10:00
committed by GitHub
parent 5060df4d5e
commit ceb1d59f82
10 changed files with 1803 additions and 1267 deletions
+15 -8
View File
@@ -613,8 +613,8 @@ pub fn elfcopy<Elf: FileHeader<Endian = Endianness>>(
for sym in &out_dynsyms {
let in_dynsym = in_dynsyms.symbol(sym.in_sym)?;
writer.write_dynamic_symbol(&object::write::elf::Sym {
name: sym.name,
section: sym.section,
section: sym.section.map(|s| s.0),
st_name: writer.dynamic_string_offset(sym.name),
st_info: in_dynsym.st_info(),
st_other: in_dynsym.st_other(),
st_shndx: in_dynsym.st_shndx(endian),
@@ -664,12 +664,15 @@ pub fn elfcopy<Elf: FileHeader<Endian = Endianness>>(
writer.write_align_gnu_verdef();
while let Some((verdef, mut verdauxs)) = verdefs.next()? {
let verdaux = verdauxs.next()?.unwrap();
let name = verdaux.name(endian, strings)?;
let name_id = writer.get_dynamic_string(name);
let verdef = object::write::elf::Verdef {
version: verdef.vd_version.get(endian),
flags: verdef.vd_flags.get(endian),
index: verdef.vd_ndx.get(endian),
aux_count: verdef.vd_cnt.get(endian),
name: writer.get_dynamic_string(verdaux.name(endian, strings)?),
name: writer.dynamic_string_offset(Some(name_id)),
hash: elf::hash(name),
};
if verdef_shared_base {
writer.write_gnu_verdef_shared(&verdef);
@@ -689,16 +692,20 @@ pub fn elfcopy<Elf: FileHeader<Endian = Endianness>>(
let strings = in_sections.strings(endian, in_data, link)?;
writer.write_align_gnu_verneed();
while let Some((verneed, mut vernauxs)) = verneeds.next()? {
let file_id = writer.get_dynamic_string(verneed.file(endian, strings)?);
writer.write_gnu_verneed(&object::write::elf::Verneed {
version: verneed.vn_version.get(endian),
aux_count: verneed.vn_cnt.get(endian),
file: writer.get_dynamic_string(verneed.file(endian, strings)?),
file: writer.dynamic_string_offset(Some(file_id)),
});
while let Some(vernaux) = vernauxs.next()? {
let name = vernaux.name(endian, strings)?;
let name_id = writer.get_dynamic_string(name);
writer.write_gnu_vernaux(&object::write::elf::Vernaux {
flags: vernaux.vna_flags.get(endian),
index: vernaux.vna_other.get(endian),
name: writer.get_dynamic_string(vernaux.name(endian, strings)?),
name: writer.dynamic_string_offset(Some(name_id)),
hash: elf::hash(name),
});
}
}
@@ -731,8 +738,8 @@ pub fn elfcopy<Elf: FileHeader<Endian = Endianness>>(
for sym in &out_syms {
let in_sym = in_syms.symbol(sym.in_sym)?;
writer.write_symbol(&object::write::elf::Sym {
name: sym.name,
section: sym.section,
section: sym.section.map(|s| s.0),
st_name: writer.string_offset(sym.name),
st_info: in_sym.st_info(),
st_other: in_sym.st_other(),
st_shndx: in_sym.st_shndx(endian),
@@ -819,7 +826,7 @@ pub fn elfcopy<Elf: FileHeader<Endian = Endianness>>(
sh_info = out_sections_index[sh_info as usize].0;
}
writer.write_section_header(&object::write::elf::SectionHeader {
name: out_section.name,
sh_name: writer.section_name_offset(out_section.name),
sh_type: in_section.sh_type(endian),
sh_flags: in_section.sh_flags(endian),
sh_addr: in_section.sh_addr(endian).into(),
+13 -9
View File
@@ -70,13 +70,13 @@ pub fn elfstub(data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
let mut st_size = sym.st_size(endian);
let st_shndx = sym.st_shndx(endian);
if sym.st_shndx(endian).index().is_some() {
section = Some(stub_section_index);
section = Some(stub_section_index.0);
st_value = stub_offset + i as u64;
st_size = 1;
}
writer.write_dynamic_symbol(&Sym {
name: id,
st_name: writer.dynamic_string_offset(id),
section,
st_info: sym.st_info(),
st_other: sym.st_other(),
@@ -122,13 +122,15 @@ pub fn elfstub(data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
let Some(verdaux) = verdauxs.next()? else {
return Err("missing verdef name".into());
};
let name = writer.add_dynamic_string(verdaux.name(endian, strings)?);
let name_bytes = verdaux.name(endian, strings)?;
let name_id = writer.add_dynamic_string(name_bytes);
writer.write_gnu_verdef(&Verdef {
version: verdef.vd_version.get(endian),
flags: verdef.vd_flags.get(endian),
index: verdef.vd_ndx.get(endian),
aux_count,
name,
name: writer.dynamic_string_offset(Some(name_id)),
hash: elf::hash(name_bytes),
});
while let Some(verdaux) = verdauxs.next()? {
let name = writer.add_dynamic_string(verdaux.name(endian, strings)?);
@@ -147,18 +149,20 @@ pub fn elfstub(data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
writer.set_gnu_verneed_count(verneed_count);
while let Some((verneed, mut vernauxs)) = verneeds.next()? {
let aux_count = vernauxs.clone().count() as u16;
let file = writer.add_dynamic_string(verneed.file(endian, strings)?);
let file_id = writer.add_dynamic_string(verneed.file(endian, strings)?);
writer.write_gnu_verneed(&Verneed {
version: verneed.vn_version.get(endian),
aux_count,
file,
file: writer.dynamic_string_offset(Some(file_id)),
});
while let Some(vernaux) = vernauxs.next()? {
let name = writer.add_dynamic_string(vernaux.name(endian, strings)?);
let name_bytes = vernaux.name(endian, strings)?;
let name_id = writer.add_dynamic_string(name_bytes);
writer.write_gnu_vernaux(&Vernaux {
flags: vernaux.vna_flags.get(endian),
index: vernaux.vna_other.get(endian),
name,
name: writer.dynamic_string_offset(Some(name_id)),
hash: elf::hash(name_bytes),
});
}
}
@@ -241,7 +245,7 @@ pub fn elfstub(data: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
// PT_LOAD maps p_vaddr == p_offset so all section addresses are equal to offsets.
writer.write_null_section_header();
let index = writer.write_section_header(&SectionHeader {
name: Some(stub_name_id),
sh_name: writer.section_name_offset(Some(stub_name_id)),
sh_type: elf::SHT_PROGBITS,
sh_flags: elf::SHF_ALLOC,
sh_addr: stub_offset,
+1 -1
View File
@@ -758,7 +758,7 @@ fn find_move_sections(
let mut blocks = Vec::new();
let file_header_size = builder.file_header_size() as u64;
let program_headers_size = (builder.program_headers_size()
+ added_segments * builder.class().program_header_size())
+ added_segments * builder.encoder().program_header_size())
as u64;
let interp = builder.interp_section();
+397 -320
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3
View File
@@ -5,5 +5,8 @@
mod object;
mod encoder;
pub use encoder::*;
mod writer;
pub use writer::*;
+4 -3
View File
@@ -1,5 +1,6 @@
use alloc::vec::Vec;
use crate::write::elf::encoder::*;
use crate::write::elf::writer::*;
use crate::write::string::StringId;
use crate::write::*;
@@ -799,11 +800,11 @@ impl<'a> Object<'a> {
SymbolSection::Absolute => (elf::SHN_ABS, None),
SymbolSection::Common => (elf::SHN_COMMON, None),
SymbolSection::Section(id) => {
(elf::SymbolSection(0), Some(section_offsets[id.0].index))
(elf::SymbolSection(0), Some(section_offsets[id.0].index.0))
}
};
writer.write_symbol(&Sym {
name: symbol_offsets[index].str_id,
st_name: writer.string_offset(symbol_offsets[index].str_id),
section,
st_info,
st_other,
@@ -880,7 +881,7 @@ impl<'a> Object<'a> {
_ => 0,
};
writer.write_section_header(&SectionHeader {
name: Some(section_offsets[index].str_id),
sh_name: writer.section_name_offset(Some(section_offsets[index].str_id)),
sh_type,
sh_flags,
sh_addr: 0,
+215 -925
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -99,6 +99,17 @@ impl<'a> StringTable<'a> {
self.offsets[id.0]
}
/// Return the offset of an optional string.
///
/// Returns 0 if `id` is `None`. Otherwise see [`Self::get_offset`].
#[allow(dead_code)]
pub(crate) fn maybe_get_offset(&self, id: Option<StringId>) -> u32 {
let Some(id) = id else {
return 0;
};
self.get_offset(id)
}
/// Append the string table to the given `Vec`, and
/// calculate the list of string offsets.
///
+11 -1
View File
@@ -281,11 +281,21 @@ pub(crate) fn align_u64(offset: u64, size: u64) -> u64 {
(offset + (size - 1)) & !(size - 1)
}
pub(crate) fn write_align(buffer: &mut dyn WritableBuffer, size: usize) {
pub(crate) fn write_align<W: WritableBuffer + ?Sized>(buffer: &mut W, size: usize) {
let new_len = align(buffer.len(), size);
buffer.resize(new_len);
}
#[allow(dead_code)]
pub(crate) fn write_pod<T: Pod, W: WritableBuffer + ?Sized>(buffer: &mut W, val: &T) {
buffer.write_bytes(bytes_of(val))
}
#[allow(dead_code)]
pub(crate) fn write_pod_slice<T: Pod, W: WritableBuffer + ?Sized>(buffer: &mut W, val: &[T]) {
buffer.write_bytes(bytes_of_slice(val))
}
#[cfg(test)]
mod tests {
use super::*;