write/elf: fix need_dynstr handling in Writer

.dynsym needs to link to .dynstr. This matches the existing logic
for .symtab and .strtab. Also some minor dynstr_len cleanup.

Writer originally required this, but was changed in f3432e98. This
change was because there are files in the wild that are missing .dynstr,
and Builder needed to be able to handle them. Now that Builder is no
longer using Writer, I don't see any downside to writing the empty
.dynstr, so I think it's better to change Writer back again.

It's still possible to omit .dynstr if you use Builder (which uses
Encoder directly, not Writer), and there is a test for this.
This commit is contained in:
Philip Craig
2026-05-30 20:19:04 +10:00
parent 165712e6a8
commit cd7aa028f4
2 changed files with 55 additions and 1 deletions
+9 -1
View File
@@ -1789,7 +1789,11 @@ impl<'a> Writer<'a, TwoPhase> {
/// Return the size of the dynamic string table.
///
/// Must be called after [`Self::reserve_dynstr`].
pub fn dynstr_len(&mut self) -> u32 {
/// Returns 0 if the dynamic string table is not needed.
pub fn dynstr_len(&self) -> u32 {
if !self.dynstr_needed() {
return 0;
}
debug_assert_ne!(self.dynstr_offset, 0);
self.dynstr_size
}
@@ -1836,6 +1840,8 @@ impl<'a> Writer<'a, TwoPhase> {
debug_assert_eq!(self.dynsym_offset, 0);
debug_assert_eq!(self.dynsym_num, 0);
self.dynsym_num = 1;
// The symbol table must link to a string table.
self.need_dynstr = true;
SymbolIndex(0)
}
@@ -1852,6 +1858,8 @@ impl<'a> Writer<'a, TwoPhase> {
debug_assert_eq!(self.dynsym_offset, 0);
if self.dynsym_num == 0 {
self.dynsym_num = 1;
// The symbol table must link to a string table.
self.need_dynstr = true;
}
let index = self.dynsym_num;
self.dynsym_num += 1;
+46
View File
@@ -109,6 +109,52 @@ fn empty_symtab() {
assert_eq!(strtab.size(), 1);
}
// .dynsym must link to .dynstr even when only the null dynamic symbol is present.
#[test]
fn empty_dynsym() {
let file_header = write::elf::FileHeader {
os_abi: elf::ELFOSABI_SYSV,
abi_version: 0,
e_type: elf::ET_DYN,
e_machine: elf::EM_X86_64,
e_entry: 0,
e_flags: elf::FileFlags(0),
};
let mut bytes = Vec::new();
let mut writer = write::elf::Writer::new(Endianness::Little, true, &mut bytes);
writer.reserve_file_header();
writer.reserve_null_dynamic_symbol_index();
writer.reserve_dynsym();
writer.reserve_dynstr().unwrap();
writer.reserve_null_section_index();
writer.reserve_dynsym_section_index();
let dynstr_index = writer.reserve_dynstr_section_index();
writer.reserve_shstrtab_section_index();
writer.reserve_shstrtab().unwrap();
writer.reserve_section_headers();
writer.write_file_header(&file_header).unwrap();
writer.write_null_dynamic_symbol();
writer.write_dynstr();
writer.write_shstrtab();
writer.write_null_section_header();
writer.write_dynsym_section_header(0, 1);
writer.write_dynstr_section_header(0);
writer.write_shstrtab_section_header();
let object = read::elf::ElfFile64::<Endianness>::parse(&*bytes).unwrap();
let dynsym = object.section_by_name(".dynsym").unwrap();
assert_eq!(dynsym.size(), 24);
let dynstr = object.section_by_name(".dynstr").unwrap();
assert_eq!(dynstr.size(), 1);
assert_eq!(
dynsym.elf_section_header().sh_link(Endianness::Little),
dynstr_index.0,
);
}
#[test]
fn aligned_sections() {
let mut object =