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`.
This commit is contained in:
Philip Craig
2026-06-08 18:49:03 +10:00
committed by GitHub
parent b87fc2f0cb
commit 6573ce5e0a
7 changed files with 53 additions and 49 deletions
+14 -13
View File
@@ -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",
))?,
+1 -1
View File
@@ -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(())
}
+13 -1
View File
@@ -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<E: Endian> Encoder<E> {
}
}
/// Write the data for a string table.
///
/// Returns the length of the written data.
pub fn strtab<W: WritableBuffer + ?Sized>(
self,
buffer: &mut W,
strtab: &mut StringTable<'_>,
) -> Result<u32> {
buffer.write_bytes(&[0]);
strtab.write(buffer, 1)
}
/// Return the size of a symbol.
pub fn sym_size(self) -> u64 {
if self.is_64 {
+12 -21
View File
@@ -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)
}
+1 -1
View File
@@ -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;
+11 -11
View File
@@ -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<K> = indexmap::IndexSet<K>;
@@ -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<u8>) -> Result<u32> {
pub fn write<W: WritableBuffer + ?Sized>(&mut self, w: &mut W, base: u32) -> Result<u32> {
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);
}
}
+1 -1
View File
@@ -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.