From 6573ce5e0a302bfd2d2c0f60aaa134cbd39f733b Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Mon, 8 Jun 2026 18:49:03 +1000 Subject: [PATCH] write/elf: avoid temp Vec for strtab in SinglePhase mode (#916) Change `StringTable::write` to allow writing directly to the WritableBuffer. Also add `Encoder::strtab`. --- src/build/elf.rs | 27 ++++++++++++++------------- src/write/coff/writer.rs | 2 +- src/write/elf/encoder.rs | 14 +++++++++++++- src/write/elf/writer.rs | 33 ++++++++++++--------------------- src/write/macho.rs | 2 +- src/write/string.rs | 22 +++++++++++----------- src/write/xcoff.rs | 2 +- 7 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/build/elf.rs b/src/build/elf.rs index fb0869c..369ac4f 100644 --- a/src/build/elf.rs +++ b/src/build/elf.rs @@ -1135,17 +1135,18 @@ impl<'data> Builder<'data> { )); } - // Build string tables. - let mut shstrtab_data = vec![0]; - shstrtab.write(1, &mut shstrtab_data)?; - let mut strtab_data = vec![0]; - strtab.write(1, &mut strtab_data)?; - let mut dynstr_data = vec![0]; - dynstr.write(1, &mut dynstr_data)?; - - // Start reserving file ranges. let encoder = self.encoder(); let address_size = u64::from(encoder.address_size()); + + // Build string tables. + let mut shstrtab_data = Vec::new(); + let shstrtab_len = encoder.strtab(&mut shstrtab_data, &mut shstrtab)?; + let mut strtab_data = Vec::new(); + let strtab_len = encoder.strtab(&mut strtab_data, &mut strtab)?; + let mut dynstr_data = Vec::new(); + let dynstr_len = encoder.strtab(&mut dynstr_data, &mut dynstr)?; + + // Start reserving file ranges. let mut offset = Offset(encoder.file_header_size()); let mut dynsym_addr = None; @@ -1236,7 +1237,7 @@ impl<'data> Builder<'data> { } SectionData::DynamicString => { dynstr_addr = Some(section.sh_addr); - offset.reserve(dynstr_data.len() as u64, 1) + offset.reserve(dynstr_len.into(), 1) } SectionData::Hash => { hash_addr = Some(section.sh_addr); @@ -1323,7 +1324,7 @@ impl<'data> Builder<'data> { out_sections[symtab_shndx_index as usize - 1].set_range(range); } if strtab_index != 0 { - let range = offset.reserve(strtab_data.len() as u64, 1); + let range = offset.reserve(strtab_len.into(), 1); out_sections[strtab_index as usize - 1].set_range(range); } @@ -1342,7 +1343,7 @@ impl<'data> Builder<'data> { } if shstrtab_index != 0 { - let range = offset.reserve(shstrtab_data.len() as u64, 1); + let range = offset.reserve(shstrtab_len.into(), 1); out_sections[shstrtab_index as usize - 1].set_range(range); } let e_shoff = if section_num != 0 { @@ -1438,7 +1439,7 @@ impl<'data> Builder<'data> { elf::DT_STRTAB => dynstr_addr.ok_or(Error::new( "Missing .dynstr section for DT_STRTAB", ))?, - elf::DT_STRSZ => dynstr_data.len() as u64, + elf::DT_STRSZ => dynstr_len.into(), elf::DT_HASH => hash_addr.ok_or(Error::new( "Missing .hash section for DT_HASH", ))?, diff --git a/src/write/coff/writer.rs b/src/write/coff/writer.rs index fd3595d..ac365a3 100644 --- a/src/write/coff/writer.rs +++ b/src/write/coff/writer.rs @@ -451,7 +451,7 @@ impl<'a> Writer<'a> { debug_assert_eq!(self.strtab_offset, 0); // First 4 bytes of strtab are the length. - self.strtab_len = self.strtab.write(4, &mut self.strtab_data)?; + self.strtab_len = self.strtab.write(&mut self.strtab_data, 4)?; self.strtab_offset = self.reserve(self.strtab_len as u64, 1); Ok(()) } diff --git a/src/write/elf/encoder.rs b/src/write/elf/encoder.rs index 423bbef..2a3a301 100644 --- a/src/write/elf/encoder.rs +++ b/src/write/elf/encoder.rs @@ -7,7 +7,7 @@ use crate::elf; use crate::endian::*; use crate::pod; use crate::write::util::{write_align, write_pod, write_pod_slice}; -use crate::write::{self, Error, Result, WritableBuffer}; +use crate::write::{self, Error, Result, StringTable, WritableBuffer}; /// Alignment for .symtab_shndx. pub const ALIGN_SYMTAB_SHNDX: u64 = 4; @@ -692,6 +692,18 @@ impl Encoder { } } + /// Write the data for a string table. + /// + /// Returns the length of the written data. + pub fn strtab( + self, + buffer: &mut W, + strtab: &mut StringTable<'_>, + ) -> Result { + buffer.write_bytes(&[0]); + strtab.write(buffer, 1) + } + /// Return the size of a symbol. pub fn sym_size(self) -> u64 { if self.is_64 { diff --git a/src/write/elf/writer.rs b/src/write/elf/writer.rs index a7b23f2..8865f5f 100644 --- a/src/write/elf/writer.rs +++ b/src/write/elf/writer.rs @@ -1282,11 +1282,8 @@ impl<'a> Writer<'a, SinglePhase> { // Thus we always write this if called. debug_assert_eq!(self.shstrtab_offset, 0); self.shstrtab_str_id = Some(self.add_section_name(b".shstrtab")); - // Start with null section name. - let mut data = vec![0]; - self.shstrtab_size = self.shstrtab.write(1, &mut data)?; self.shstrtab_offset = self.offset(); - self.write(&data); + self.shstrtab_size = self.encoder.strtab(self.buffer, &mut self.shstrtab)?; Ok((self.shstrtab_offset, self.shstrtab_size)) } @@ -1301,11 +1298,8 @@ impl<'a> Writer<'a, SinglePhase> { } debug_assert_eq!(self.strtab_offset, 0); self.strtab_str_id = Some(self.add_section_name(b".strtab")); - // Start with null section name. - let mut data = vec![0]; - self.strtab_size = self.strtab.write(1, &mut data)?; self.strtab_offset = self.offset(); - self.write(&data); + self.strtab_size = self.encoder.strtab(self.buffer, &mut self.strtab)?; Ok((self.strtab_offset, self.strtab_size)) } @@ -1320,11 +1314,8 @@ impl<'a> Writer<'a, SinglePhase> { } debug_assert_eq!(self.dynstr_offset, 0); self.dynstr_str_id = Some(self.add_section_name(b".dynstr")); - // Start with null section name. - let mut data = vec![0]; - self.dynstr_size = self.dynstr.write(1, &mut data)?; self.dynstr_offset = self.offset(); - self.write(&data); + self.dynstr_size = self.encoder.strtab(self.buffer, &mut self.dynstr)?; Ok((self.dynstr_offset, self.dynstr_size)) } @@ -1554,9 +1545,9 @@ impl<'a> Writer<'a, TwoPhase> { if self.layout.section_num == 0 { return Ok(()); } - // Start with null section name. - self.mode.shstrtab_data = vec![0]; - self.shstrtab_size = self.shstrtab.write(1, &mut self.mode.shstrtab_data)?; + self.shstrtab_size = self + .encoder + .strtab(&mut self.mode.shstrtab_data, &mut self.shstrtab)?; self.shstrtab_offset = self.reserve(self.shstrtab_size as u64, 1); Ok(()) } @@ -1607,9 +1598,9 @@ impl<'a> Writer<'a, TwoPhase> { if !self.strtab_needed() { return Ok(()); } - // Start with null string. - self.mode.strtab_data = vec![0]; - self.strtab_size = self.strtab.write(1, &mut self.mode.strtab_data)?; + self.strtab_size = self + .encoder + .strtab(&mut self.mode.strtab_data, &mut self.strtab)?; self.strtab_offset = self.reserve(self.strtab_size as u64, 1); Ok(()) } @@ -1789,9 +1780,9 @@ impl<'a> Writer<'a, TwoPhase> { if !self.dynstr_needed() { return Ok(0); } - // Start with null string. - self.mode.dynstr_data = vec![0]; - self.dynstr_size = self.dynstr.write(1, &mut self.mode.dynstr_data)?; + self.dynstr_size = self + .encoder + .strtab(&mut self.mode.dynstr_data, &mut self.dynstr)?; self.dynstr_offset = self.reserve(self.dynstr_size as u64, 1); Ok(self.dynstr_offset) } diff --git a/src/write/macho.rs b/src/write/macho.rs index 7f35cdf..3e7134e 100644 --- a/src/write/macho.rs +++ b/src/write/macho.rs @@ -603,7 +603,7 @@ impl<'a> Object<'a> { let strtab_offset = offset; // Start with null name. let mut strtab_data = vec![0]; - strtab.write(1, &mut strtab_data)?; + strtab.write(&mut strtab_data, 1)?; write_align(&mut strtab_data, pointer_align); offset += strtab_data.len() as u64; diff --git a/src/write/string.rs b/src/write/string.rs index c8ab37d..0eb2b4a 100644 --- a/src/write/string.rs +++ b/src/write/string.rs @@ -1,6 +1,6 @@ use alloc::vec::Vec; -use crate::write::{Error, Result}; +use crate::write::{Error, Result, WritableBuffer}; #[cfg(feature = "write_std")] type IndexSet = indexmap::IndexSet; @@ -104,7 +104,7 @@ impl<'a> StringTable<'a> { self.get_offset(id) } - /// Append the string table to the given `Vec`, and + /// Append the string table to the given buffer, and /// calculate the list of string offsets. /// /// `base` is the initial string table offset. For example, @@ -117,7 +117,7 @@ impl<'a> StringTable<'a> { /// - `write` has already been called /// - any string contains a null byte /// - the string table size is > `u32::MAX` - pub fn write(&mut self, base: u32, w: &mut Vec) -> Result { + pub fn write(&mut self, w: &mut W, base: u32) -> Result { if !self.in_order && !self.offsets.is_empty() { return Err(Error("string table already written".into())); } @@ -128,8 +128,8 @@ impl<'a> StringTable<'a> { if self.in_order { debug_assert_eq!(self.base, base); for string in &self.strings { - w.extend_from_slice(string); - w.push(0); + w.write_bytes(string); + w.write_bytes(&[0]); } return u32::try_from(self.size) .map_err(|_| Error("string table size overflow".into())); @@ -148,8 +148,8 @@ impl<'a> StringTable<'a> { self.offsets[id] = (offset - len) as u32; } else { self.offsets[id] = offset as u32; - w.extend_from_slice(string); - w.push(0); + w.write_bytes(string); + w.write_bytes(&[0]); offset += len; previous = string; } @@ -253,7 +253,7 @@ mod tests { let id3 = table.add(b"foobar"); let mut data = vec![0]; - assert_eq!(table.write(1, &mut data), Ok(12)); + assert_eq!(table.write(&mut data, 1), Ok(12)); assert_eq!(data, b"\0foobar\0foo\0"); assert_eq!(table.get_offset(id0), 11); @@ -262,7 +262,7 @@ mod tests { assert_eq!(table.get_offset(id3), 1); let mut data = Vec::new(); - assert!(table.write(1, &mut data).is_err()); + assert!(table.write(&mut data, 1).is_err()); } #[test] @@ -274,7 +274,7 @@ mod tests { let id3 = table.add(b"foobar"); let mut data = vec![0]; - assert_eq!(table.write(1, &mut data), Ok(17)); + assert_eq!(table.write(&mut data, 1), Ok(17)); assert_eq!(data, b"\0\0foo\0bar\0foobar\0"); assert_eq!(table.get_offset(id0), 1); @@ -284,7 +284,7 @@ mod tests { // Not documented or expected to be needed, but multiple writes aren't prevented. let mut data2 = vec![0]; - assert_eq!(table.write(1, &mut data2), Ok(17)); + assert_eq!(table.write(&mut data2, 1), Ok(17)); assert_eq!(data, data2); } } diff --git a/src/write/xcoff.rs b/src/write/xcoff.rs index 746c148..42fff85 100644 --- a/src/write/xcoff.rs +++ b/src/write/xcoff.rs @@ -349,7 +349,7 @@ impl<'a> Object<'a> { let strtab_offset = offset; let mut strtab_data = Vec::new(); // First 4 bytes of strtab are the length. - let strtab_len = strtab.write(4, &mut strtab_data)?; + let strtab_len = strtab.write(&mut strtab_data, 4)?; offset += strtab_len as u64; // Start writing.