diff --git a/crates/examples/src/elfcopy.rs b/crates/examples/src/elfcopy.rs index a5865ab..506d3b2 100644 --- a/crates/examples/src/elfcopy.rs +++ b/crates/examples/src/elfcopy.rs @@ -613,8 +613,8 @@ pub fn elfcopy>( 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>( 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>( 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>( 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>( 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(), diff --git a/crates/examples/src/elfstub.rs b/crates/examples/src/elfstub.rs index 2f07111..2e352da 100644 --- a/crates/examples/src/elfstub.rs +++ b/crates/examples/src/elfstub.rs @@ -70,13 +70,13 @@ pub fn elfstub(data: &[u8]) -> Result, Box> { 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, Box> { 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, Box> { 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, Box> { // 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, diff --git a/crates/rewrite/src/elf.rs b/crates/rewrite/src/elf.rs index a7c97cd..507b963 100644 --- a/crates/rewrite/src/elf.rs +++ b/crates/rewrite/src/elf.rs @@ -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(); diff --git a/src/build/elf.rs b/src/build/elf.rs index f8fce4e..0eb1849 100644 --- a/src/build/elf.rs +++ b/src/build/elf.rs @@ -731,8 +731,15 @@ impl<'data> Builder<'data> { id: SectionId, name: Option, offset: u64, + size: u64, attributes: Vec, } + impl SectionOut { + fn set_range(&mut self, range: (u64, u64)) { + self.offset = range.0; + self.size = range.1; + } + } struct SymbolOut { id: SymbolId, @@ -751,143 +758,147 @@ impl<'data> Builder<'data> { versions: Vec, } + struct Offset(u64); + impl Offset { + fn reserve(&mut self, size: u64, align: u64) -> (u64, u64) { + self.0 = write::align_u64(self.0, align); + let offset = self.0; + self.0 += size; + (offset, size) + } + } + // TODO: require the caller to do this? self.delete_orphans(); self.delete_unused_versions(); - let mut writer = write::elf::Writer::new(self.endian, self.is_64, buffer); - // Find metadata sections, and assign section indices. - let mut shstrtab_id = None; - let mut symtab_id = None; - let mut symtab_shndx_id = None; - let mut strtab_id = None; - let mut dynsym_id = None; - let mut dynstr_id = None; - let mut hash_id = None; - let mut gnu_hash_id = None; - let mut gnu_versym_id = None; - let mut gnu_verdef_id = None; - let mut gnu_verneed_id = None; + let mut shstrtab_index = 0; + let mut symtab_index = 0; + let mut symtab_shndx_index = 0; + let mut strtab_index = 0; + let mut dynsym_index = 0; + let mut dynstr_index = 0; + let mut hash_index = 0; + let mut gnu_hash_index = 0; + let mut gnu_versym_index = 0; + let mut gnu_verdef_index = 0; + let mut gnu_verneed_index = 0; let mut out_sections = Vec::with_capacity(self.sections.len()); - let mut out_sections_index = vec![None; self.sections.len()]; + let mut out_sections_index = vec![0; self.sections.len()]; + let mut section_num = 0; if !self.sections.is_empty() { - writer.reserve_null_section_index(); + section_num = 1; } + let mut shstrtab = write::string::StringTable::new(); for section in &self.sections { - let index = match §ion.data { + let index = section_num; + out_sections_index[section.id.0] = index; + section_num += 1; + + match §ion.data { SectionData::Data(_) | SectionData::UninitializedData(_) | SectionData::Relocation(_) | SectionData::DynamicRelocation(_) | SectionData::Note(_) | SectionData::Dynamic(_) - | SectionData::Attributes(_) => writer.reserve_section_index(), + | SectionData::Attributes(_) => {} SectionData::SectionString => { - if shstrtab_id.is_some() { + if shstrtab_index != 0 { return Err(Error::new("Multiple .shstrtab sections")); } - shstrtab_id = Some(section.id); - writer.reserve_shstrtab_section_index_with_name(§ion.name) + shstrtab_index = index; } SectionData::Symbol => { - if symtab_id.is_some() { + if symtab_index != 0 { return Err(Error::new("Multiple .symtab sections")); } - symtab_id = Some(section.id); - writer.reserve_symtab_section_index_with_name(§ion.name) + symtab_index = index; } SectionData::SymbolSectionIndex => { - if symtab_shndx_id.is_some() { + if symtab_shndx_index != 0 { return Err(Error::new("Multiple .symtab_shndx sections")); } - symtab_shndx_id = Some(section.id); - writer.reserve_symtab_shndx_section_index_with_name(§ion.name) + symtab_shndx_index = index; } SectionData::String => { - if strtab_id.is_some() { + if strtab_index != 0 { return Err(Error::new("Multiple .strtab sections")); } - strtab_id = Some(section.id); - writer.reserve_strtab_section_index_with_name(§ion.name) + strtab_index = index; } SectionData::DynamicSymbol => { - if dynsym_id.is_some() { + if dynsym_index != 0 { return Err(Error::new("Multiple .dynsym sections")); } - dynsym_id = Some(section.id); - writer.reserve_dynsym_section_index_with_name(§ion.name) + dynsym_index = index; } SectionData::DynamicString => { - if dynstr_id.is_some() { + if dynstr_index != 0 { return Err(Error::new("Multiple .dynstr sections")); } - dynstr_id = Some(section.id); - writer.reserve_dynstr_section_index_with_name(§ion.name) + dynstr_index = index; } SectionData::Hash => { - if hash_id.is_some() { + if hash_index != 0 { return Err(Error::new("Multiple .hash sections")); } - hash_id = Some(section.id); - writer.reserve_hash_section_index_with_name(§ion.name) + hash_index = index; } SectionData::GnuHash => { - if gnu_hash_id.is_some() { + if gnu_hash_index != 0 { return Err(Error::new("Multiple .gnu.hash sections")); } - gnu_hash_id = Some(section.id); - writer.reserve_gnu_hash_section_index_with_name(§ion.name) + gnu_hash_index = index; } SectionData::GnuVersym => { - if gnu_versym_id.is_some() { + if gnu_versym_index != 0 { return Err(Error::new("Multiple .gnu.version sections")); } - gnu_versym_id = Some(section.id); - writer.reserve_gnu_versym_section_index_with_name(§ion.name) + gnu_versym_index = index; } SectionData::GnuVerdef => { - if gnu_verdef_id.is_some() { + if gnu_verdef_index != 0 { return Err(Error::new("Multiple .gnu.version_d sections")); } - gnu_verdef_id = Some(section.id); - writer.reserve_gnu_verdef_section_index_with_name(§ion.name) + gnu_verdef_index = index; } SectionData::GnuVerneed => { - if gnu_verneed_id.is_some() { + if gnu_verneed_index != 0 { return Err(Error::new("Multiple .gnu.version_r sections")); } - gnu_verneed_id = Some(section.id); - writer.reserve_gnu_verneed_section_index_with_name(§ion.name) + gnu_verneed_index = index; } }; - out_sections_index[section.id.0] = Some(index); let name = if section.name.is_empty() { None } else { - Some(writer.add_section_name(§ion.name)) + Some(shstrtab.add(§ion.name)) }; out_sections.push(SectionOut { id: section.id, name, offset: 0, + size: 0, attributes: Vec::new(), }); } - // Assign dynamic strings. + // Add dynamic strings to string table. + let mut dynstr = write::string::StringTable::new(); for section in &self.sections { if let SectionData::Dynamic(dynamics) = §ion.data { for dynamic in dynamics { if let Dynamic::String { val, .. } = dynamic { - writer.add_dynamic_string(val); + dynstr.add(val); } } } } - // Assign dynamic symbol indices. + // Assign dynamic symbol indices and add symbol names to string table. let mut out_dynsyms = Vec::with_capacity(self.dynamic_symbols.len()); // Local symbols must come before global. let local_symbols = self @@ -903,11 +914,11 @@ impl<'data> Builder<'data> { let mut hash = None; let mut gnu_hash = None; if !symbol.name.is_empty() { - name = Some(writer.add_dynamic_string(&symbol.name)); - if hash_id.is_some() { + name = Some(dynstr.add(&symbol.name)); + if hash_index != 0 { hash = Some(elf::hash(&symbol.name)); } - if gnu_hash_id.is_some() + if gnu_hash_index != 0 && (symbol.section.is_some() || symbol.st_shndx != elf::SHN_UNDEF) { gnu_hash = Some(elf::gnu_hash(&symbol.name)); @@ -926,7 +937,7 @@ impl<'data> Builder<'data> { .count(); // We must sort for GNU hash before allocating symbol indices. let mut gnu_hash_symbol_count = 0; - if gnu_hash_id.is_some() { + if gnu_hash_index != 0 { if self.gnu_hash_bucket_count == 0 { return Err(Error::new(".gnu.hash bucket count is zero")); } @@ -941,12 +952,14 @@ impl<'data> Builder<'data> { .skip_while(|sym| sym.gnu_hash.is_none()) .count() as u32; } - let mut out_dynsyms_index = vec![None; self.dynamic_symbols.len()]; - if dynsym_id.is_some() { - writer.reserve_null_dynamic_symbol_index(); + let mut out_dynsyms_index = vec![0; self.dynamic_symbols.len()]; + let mut dynsym_num = 0u32; + if dynsym_index != 0 { + dynsym_num = 1; } for out_dynsym in &mut out_dynsyms { - out_dynsyms_index[out_dynsym.id.0] = Some(writer.reserve_dynamic_symbol_index()); + out_dynsyms_index[out_dynsym.id.0] = dynsym_num; + dynsym_num += 1; } // Hash parameters. @@ -959,10 +972,18 @@ impl<'data> Builder<'data> { } else { out_dynsyms.len() as u32 - gnu_hash_symbol_count }; - let gnu_hash_symbol_base = gnu_hash_index_base + 1; // Null symbol. + let gnu_hash_table = write::elf::GnuHashTable { + bucket_count: self.gnu_hash_bucket_count, + bloom_shift: self.gnu_hash_bloom_shift, + bloom_count: self.gnu_hash_bloom_count, + symbol_base: gnu_hash_index_base + 1, // Null symbol. + symbol_count: gnu_hash_symbol_count, + }; - // Assign symbol indices. + // Assign symbol indices and add names to string table. let mut out_syms = Vec::with_capacity(self.symbols.len()); + let mut strtab = write::string::StringTable::new(); + let mut need_symtab_shndx = symtab_shndx_index != 0; // Local symbols must come before global. let local_symbols = self .symbols @@ -976,9 +997,13 @@ impl<'data> Builder<'data> { let name = if symbol.name.is_empty() { None } else { - Some(writer.add_string(&symbol.name)) + Some(strtab.add(&symbol.name)) }; - + if let Some(section_id) = symbol.section { + if out_sections_index[section_id.0] >= elf::SHN_LORESERVE.into() { + need_symtab_shndx = true; + } + } out_syms.push(SymbolOut { id: symbol.id, name, @@ -988,12 +1013,14 @@ impl<'data> Builder<'data> { .iter() .take_while(|sym| self.symbols.get(sym.id).st_bind() == elf::STB_LOCAL) .count(); - let mut out_syms_index = vec![None; self.symbols.len()]; - if symtab_id.is_some() { - writer.reserve_null_symbol_index(); + let mut out_syms_index = vec![0; self.symbols.len()]; + let mut sym_num = 0; + if symtab_index != 0 { + sym_num = 1; } for out_sym in out_syms.iter_mut() { - out_syms_index[out_sym.id.0] = Some(writer.reserve_symbol_index(None)); + out_syms_index[out_sym.id.0] = sym_num; + sym_num += 1; } // Count the versions and add version strings. @@ -1006,7 +1033,7 @@ impl<'data> Builder<'data> { if let Some(version_base) = &self.version_base { verdef_count += 1; verdaux_count += 1; - writer.add_dynamic_string(version_base); + dynstr.add(version_base); } for version in &self.versions { match &version.data { @@ -1016,21 +1043,21 @@ impl<'data> Builder<'data> { } else { verdaux_count += def.names.len(); for name in &def.names { - writer.add_dynamic_string(name); + dynstr.add(name); } } verdef_count += 1; } VersionData::Need(need) => { vernaux_count += 1; - writer.add_dynamic_string(&need.name); + dynstr.add(&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); + dynstr.add(&file.name); } // Build the attributes sections. @@ -1042,7 +1069,7 @@ impl<'data> Builder<'data> { if attributes.subsections.is_empty() { continue; } - let mut writer = writer.attributes_writer(); + let mut writer = write::elf::AttributesWriter::new(self.endian); for subsection in &attributes.subsections { writer.start_subsection(&subsection.vendor); for subsubsection in &subsection.subsubsections { @@ -1051,16 +1078,18 @@ impl<'data> Builder<'data> { AttributeScope::File => {} AttributeScope::Section(sections) => { for id in sections { - if let Some(index) = out_sections_index[id.0] { - writer.write_subsubsection_index(index.0); + let index = out_sections_index[id.0]; + if index != 0 { + writer.write_subsubsection_index(index); } } writer.write_subsubsection_index(0); } AttributeScope::Symbol(symbols) => { for id in symbols { - if let Some(index) = out_syms_index[id.0] { - writer.write_subsubsection_index(index.0); + let index = out_syms_index[id.0]; + if index != 0 { + writer.write_subsubsection_index(index); } } writer.write_subsubsection_index(0); @@ -1075,45 +1104,49 @@ impl<'data> Builder<'data> { } // TODO: support section headers in strtab - if shstrtab_id.is_none() && !out_sections.is_empty() { + if shstrtab_index == 0 && section_num != 0 { return Err(Error::new(".shstrtab section is needed but not present")); } - if symtab_id.is_none() && !out_syms.is_empty() { + if symtab_index == 0 && sym_num != 0 { return Err(Error::new(".symtab section is needed but not present")); } - if symtab_shndx_id.is_none() && writer.symtab_shndx_needed() { + if symtab_shndx_index == 0 && need_symtab_shndx { return Err(Error::new( ".symtab.shndx section is needed but not present", )); - } else if symtab_shndx_id.is_some() { - writer.require_symtab_shndx(); } - if strtab_id.is_none() && writer.strtab_needed() { + if strtab_index == 0 && !strtab.is_empty() { return Err(Error::new(".strtab section is needed but not present")); - } else if strtab_id.is_some() { - writer.require_strtab(); } - if dynsym_id.is_none() && !out_dynsyms.is_empty() { + if dynsym_index == 0 && dynsym_num != 0 { return Err(Error::new(".dynsym section is needed but not present")); } - if dynstr_id.is_none() && writer.dynstr_needed() { + if dynstr_index == 0 && !dynstr.is_empty() { return Err(Error::new(".dynstr section is needed but not present")); - } else if dynstr_id.is_some() { - writer.require_dynstr(); } - if gnu_verdef_id.is_none() && verdef_count > 0 { + if gnu_verdef_index == 0 && 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 { + if gnu_verneed_index == 0 && verneed_count != 0 { return Err(Error::new( ".gnu.version_r section is needed but not present", )); } + // 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. - writer.reserve_file_header(); + let encoder = self.encoder(); + let address_size = u64::from(encoder.address_size()); + let mut offset = Offset(encoder.file_header_size() as u64); let mut dynsym_addr = None; let mut dynstr_addr = None; @@ -1123,15 +1156,18 @@ impl<'data> Builder<'data> { let mut verdef_addr = None; let mut verneed_addr = None; - if !self.segments.is_empty() { + let segment_num = self.segments.count() as u32; + let mut e_phoff = 0; + if segment_num != 0 { + let size = u64::from(segment_num) * encoder.program_header_size() as u64; + e_phoff = offset.reserve(size, address_size).0; // TODO: support program headers in other locations. - if self.header.e_phoff != writer.reserved_len() as u64 { + if self.header.e_phoff != e_phoff { return Err(Error(format!( "Unsupported e_phoff value 0x{:x}", self.header.e_phoff ))); } - writer.reserve_program_headers(self.segments.count() as u32); } let mut alloc_sections = Vec::new(); @@ -1160,65 +1196,76 @@ impl<'data> Builder<'data> { let out_section = &mut out_sections[*index]; let section = &self.sections.get(out_section.id); - if section.sh_type == elf::SHT_NOBITS { + if let SectionData::UninitializedData(len) = §ion.data { // sh_offset is meaningless for SHT_NOBITS, so preserve the input // value without checking it. out_section.offset = section.sh_offset; + out_section.size = *len; continue; } - if section.sh_offset < writer.reserved_len() as u64 { + if section.sh_offset < offset.0 { 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(), + section.sh_offset, section.name, offset.0, ))); } // 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 { + offset.0 = section.sh_offset; + out_section.set_range(match §ion.data { SectionData::Data(data) => { - writer.reserve(data.len(), section.sh_addralign as usize) + offset.reserve(data.len() as u64, section.sh_addralign) + } + SectionData::DynamicRelocation(relocations) => { + let size = relocations.len() as u64 + * encoder.rel_size(section.sh_type == elf::SHT_RELA) as u64; + offset.reserve(size, address_size) } - 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) + offset.reserve(data.len() as u64, section.sh_addralign) + } + SectionData::Dynamic(dynamics) => { + let size = (1 + dynamics.len() as u64) * encoder.dyn_size() as u64; + offset.reserve(size, address_size) } - SectionData::Dynamic(dynamics) => writer.reserve_dynamics(1 + dynamics.len()), SectionData::DynamicSymbol => { dynsym_addr = Some(section.sh_addr); - writer.reserve_dynsym() + let size = u64::from(dynsym_num) * encoder.sym_size() as u64; + offset.reserve(size, address_size) } SectionData::DynamicString => { dynstr_addr = Some(section.sh_addr); - writer.reserve_dynstr()? + offset.reserve(dynstr_data.len() as u64, 1) } SectionData::Hash => { hash_addr = Some(section.sh_addr); - writer.reserve_hash(self.hash_bucket_count, hash_chain_count) + let size = encoder.hash_size(self.hash_bucket_count, hash_chain_count); + offset.reserve(size as u64, write::elf::ALIGN_HASH.into()) } SectionData::GnuHash => { gnu_hash_addr = Some(section.sh_addr); - writer.reserve_gnu_hash( + let size = encoder.gnu_hash_size( self.gnu_hash_bloom_count, self.gnu_hash_bucket_count, gnu_hash_symbol_count, - ) + ); + offset.reserve(size as u64, address_size) } SectionData::GnuVersym => { versym_addr = Some(section.sh_addr); - writer.reserve_gnu_versym() + let size = encoder.gnu_versym_size(dynsym_num as usize); + offset.reserve(size as u64, write::elf::ALIGN_GNU_VERSYM.into()) } SectionData::GnuVerdef => { verdef_addr = Some(section.sh_addr); - writer.reserve_gnu_verdef(verdef_count, verdaux_count) + let size = encoder.gnu_verdef_size(verdef_count, verdaux_count); + offset.reserve(size as u64, write::elf::ALIGN_GNU_VERDEF.into()) } SectionData::GnuVerneed => { verneed_addr = Some(section.sh_addr); - writer.reserve_gnu_verneed(verneed_count, vernaux_count) + let size = encoder.gnu_verneed_size(verneed_count, vernaux_count); + offset.reserve(size as u64, write::elf::ALIGN_GNU_VERNEED.into()) } _ => { return Err(Error(format!( @@ -1226,7 +1273,7 @@ impl<'data> Builder<'data> { section.sh_type, section.name, ))); } - }; + }); if out_section.offset != section.sh_offset { return Err(Error(format!( "Unaligned sh_offset value 0x{:x} for section '{}', expected 0x{:x}", @@ -1242,16 +1289,12 @@ impl<'data> Builder<'data> { 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() as u64, - SectionData::Note(data) => { - writer.reserve(data.len(), section.sh_addralign as usize) - } + out_section.set_range(match §ion.data { + SectionData::Data(data) => offset.reserve(data.len() as u64, section.sh_addralign), + SectionData::UninitializedData(len) => (offset.0, *len), + SectionData::Note(data) => offset.reserve(data.len() as u64, section.sh_addralign), SectionData::Attributes(_) => { - writer.reserve(out_section.attributes.len(), section.sh_addralign as usize) + offset.reserve(out_section.attributes.len() as u64, section.sh_addralign) } // These are handled elsewhere. SectionData::Relocation(_) @@ -1267,12 +1310,22 @@ impl<'data> Builder<'data> { section.sh_type ))); } - }; + }) } - writer.reserve_symtab(); - writer.reserve_symtab_shndx(); - writer.reserve_strtab()?; + if symtab_index != 0 { + let size = u64::from(sym_num) * encoder.sym_size() as u64; + let range = offset.reserve(size, address_size); + out_sections[symtab_index as usize - 1].set_range(range); + } + if symtab_shndx_index != 0 { + let range = offset.reserve(u64::from(sym_num) * 4, 4); + 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); + out_sections[strtab_index as usize - 1].set_range(range); + } // Reserve non-alloc relocations. for out_section in &mut out_sections { @@ -1283,36 +1336,56 @@ impl<'data> Builder<'data> { let SectionData::Relocation(relocations) = §ion.data else { continue; }; - out_section.offset = - writer.reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA); + let size = relocations.len() as u64 + * encoder.rel_size(section.sh_type == elf::SHT_RELA) as u64; + out_section.set_range(offset.reserve(size, address_size)); } - writer.reserve_shstrtab()?; - writer.reserve_section_headers(); + if shstrtab_index != 0 { + let range = offset.reserve(shstrtab_data.len() as u64, 1); + out_sections[shstrtab_index as usize - 1].set_range(range); + } + let e_shoff = if section_num != 0 { + let size = section_num as u64 * encoder.section_header_size() as u64; + offset.reserve(size, address_size).0 + } else { + 0 + }; // Start writing. - writer.write_file_header(&write::elf::FileHeader { + let header = write::elf::FileHeader { os_abi: self.header.os_abi, abi_version: self.header.abi_version, e_type: self.header.e_type, e_machine: self.header.e_machine, e_entry: self.header.e_entry, e_flags: self.header.e_flags, - })?; + }; + let layout = write::elf::FileHeaderLayout { + e_phoff, + segment_num, + e_shoff, + section_num, + shstrtab_index, + }; + encoder.file_header(buffer, &header, &layout)?; if !self.segments.is_empty() { - writer.write_align_program_headers(); + encoder.address_align(buffer); 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, - }); + encoder.program_header( + buffer, + &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, + }, + ); } } @@ -1326,19 +1399,20 @@ impl<'data> Builder<'data> { continue; } - writer.pad_until(out_section.offset); + buffer.resize(out_section.offset as usize); match §ion.data { SectionData::Data(data) => { - writer.write(data); + buffer.write_bytes(data); } SectionData::DynamicRelocation(relocations) => { for rel in relocations { let r_sym = if let Some(symbol) = rel.symbol { - out_dynsyms_index[symbol.0].unwrap().0 + out_dynsyms_index[symbol.0] } else { 0 }; - writer.write_relocation( + encoder.relocation( + buffer, section.sh_type == elf::SHT_RELA, &write::elf::Rel { r_offset: rel.r_offset, @@ -1350,7 +1424,7 @@ impl<'data> Builder<'data> { } } SectionData::Note(data) => { - writer.write(data); + buffer.write_bytes(data); } SectionData::Dynamic(dynamics) => { for d in dynamics { @@ -1364,7 +1438,7 @@ impl<'data> Builder<'data> { elf::DT_STRTAB => dynstr_addr.ok_or(Error::new( "Missing .dynstr section for DT_STRTAB", ))?, - elf::DT_STRSZ => writer.dynstr_len().into(), + elf::DT_STRSZ => dynstr_data.len() as u64, elf::DT_HASH => hash_addr.ok_or(Error::new( "Missing .hash section for DT_HASH", ))?, @@ -1389,134 +1463,148 @@ impl<'data> Builder<'data> { ))); } }; - writer.write_dynamic(tag, val)?; + encoder.dynamic(buffer, tag, val)?; } Dynamic::Integer { tag, val } => { - writer.write_dynamic(tag, val)?; + encoder.dynamic(buffer, tag, val)?; } Dynamic::String { tag, ref val } => { - let val = writer.get_dynamic_string(val); - writer.write_dynamic_string(tag, val)?; + let val = dynstr.get_offset(dynstr.get_id(val)); + encoder.dynamic(buffer, tag, val.into())?; } } } - writer.write_dynamic(elf::DT_NULL, 0)?; + encoder.dynamic(buffer, elf::DT_NULL, 0)?; } SectionData::DynamicSymbol => { - writer.write_null_dynamic_symbol(); + encoder.null_symbol(buffer); 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, - }); + let section = symbol.section.map(|id| out_sections_index[id.0]); + encoder.symbol( + buffer, + &write::elf::Sym { + st_name: dynstr.maybe_get_offset(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(); + buffer.write_bytes(&dynstr_data); } SectionData::Hash => { if self.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 => { - if self.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, + encoder.hash_table( + buffer, + self.hash_bucket_count, + hash_chain_count, |index| { - out_dynsyms[(gnu_hash_index_base + index) as usize] - .gnu_hash - .unwrap() + out_dynsyms + .get(index.checked_sub(hash_index_base)? as usize)? + .hash }, ); } + SectionData::GnuHash => { + if gnu_hash_table.bucket_count == 0 { + return Err(Error::new(".gnu.hash bucket count is zero")); + } + encoder.gnu_hash_table(buffer, &gnu_hash_table, |index| { + out_dynsyms[(gnu_hash_index_base + index) as usize] + .gnu_hash + .unwrap() + }); + } SectionData::GnuVersym => { - writer.write_null_gnu_versym(); + encoder.gnu_versym(buffer, elf::VER_NDX_LOCAL.into()); for out_dynsym in &out_dynsyms { let symbol = self.dynamic_symbols.get(out_dynsym.id); let index = elf::VersymIndex::new( symbol.version.index(), symbol.version_hidden, ); - writer.write_gnu_versym(index); + encoder.gnu_versym(buffer, index); } } SectionData::GnuVerdef => { - writer.write_align_gnu_verdef(); + let mut count = verdef_count; if let Some(version_base) = &self.version_base { + count -= 1; let verdef = write::elf::Verdef { version: elf::VER_DEF_CURRENT, flags: elf::VER_FLG_BASE, index: elf::VER_NDX_GLOBAL, aux_count: 1, - name: writer.get_dynamic_string(version_base), + name: dynstr.get_offset(dynstr.get_id(version_base)), + hash: elf::hash(version_base), }; if verdef_shared_base { - writer.write_gnu_verdef_shared(&verdef); + encoder.gnu_verdef_shared(buffer, &verdef); } else { - writer.write_gnu_verdef(&verdef); + encoder.gnu_verdef(buffer, count != 0, &verdef); } } for version in &self.versions { if let VersionData::Def(def) = &version.data { + count -= 1; let mut 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 { + let verdef = write::elf::Verdef { version: elf::VER_DEF_CURRENT, flags: def.flags, index: elf::VersionIndex(version.id.0 as u16), aux_count: def.names.len() as u16, - name: writer.get_dynamic_string(name), - }); + name: dynstr.get_offset(dynstr.get_id(name)), + hash: elf::hash(name), + }; + encoder.gnu_verdef(buffer, count != 0, &verdef); + let mut aux_count = names.len(); for name in names { - writer.write_gnu_verdaux(writer.get_dynamic_string(name)); + aux_count -= 1; + encoder.gnu_verdaux( + buffer, + aux_count != 0, + dynstr.get_offset(dynstr.get_id(name)), + ); } } } } SectionData::GnuVerneed => { - writer.write_align_gnu_verneed(); + let mut count = verneed_count; 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 { + count -= 1; + let verneed = write::elf::Verneed { version: elf::VER_NEED_CURRENT, aux_count: out_file.versions.len() as u16, - file: writer.get_dynamic_string(&file.name), - }); + file: dynstr.get_offset(dynstr.get_id(&file.name)), + }; + encoder.gnu_verneed(buffer, count != 0, &verneed); + let mut aux_count = out_file.versions.len(); for id in &out_file.versions { + aux_count -= 1; let version = self.versions.get(*id); // This will always match. if let VersionData::Need(need) = &version.data { debug_assert_eq!(*id, version.id); - writer.write_gnu_vernaux(&write::elf::Vernaux { + let vernaux = write::elf::Vernaux { flags: need.flags, index: elf::VersionIndex(version.id.0 as u16), - name: writer.get_dynamic_string(&need.name), - }); + name: dynstr.get_offset(dynstr.get_id(&need.name)), + hash: elf::hash(&need.name), + }; + encoder.gnu_vernaux(buffer, aux_count != 0, &vernaux); } } } @@ -1539,22 +1627,19 @@ impl<'data> Builder<'data> { } match §ion.data { SectionData::Data(data) => { - writer.write_align(section.sh_addralign as usize); - debug_assert_eq!(out_section.offset, writer.offset()); - writer.write(data); + buffer.resize(out_section.offset as usize); + buffer.write_bytes(data); } SectionData::UninitializedData(_) => { // Nothing to do. } SectionData::Note(data) => { - writer.write_align(section.sh_addralign as usize); - debug_assert_eq!(out_section.offset, writer.offset()); - writer.write(data); + buffer.resize(out_section.offset as usize); + buffer.write_bytes(data); } SectionData::Attributes(_) => { - writer.write_align(section.sh_addralign as usize); - debug_assert_eq!(out_section.offset, writer.offset()); - writer.write(&out_section.attributes); + buffer.resize(out_section.offset as usize); + buffer.write_bytes(&out_section.attributes); } // These are handled elsewhere. SectionData::Relocation(_) @@ -1571,56 +1656,81 @@ impl<'data> Builder<'data> { } } - writer.write_null_symbol(); + let mut symtab_shndx_data = Vec::new(); + if symtab_index != 0 { + let out_section = &out_sections[symtab_index as usize - 1]; + buffer.resize(out_section.offset as usize); + encoder.null_symbol(buffer); + if need_symtab_shndx { + encoder.u32(&mut symtab_shndx_data, 0u32); + } + } 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, + let sym = write::elf::Sym { + section: symbol.section.map(|id| out_sections_index[id.0]), + st_name: strtab.maybe_get_offset(out_sym.name), 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, - }); + }; + let index = encoder.symbol(buffer, &sym); + if need_symtab_shndx { + encoder.u32(&mut symtab_shndx_data, index.unwrap_or(0)); + } + } + if symtab_shndx_index != 0 { + let out_section = &out_sections[symtab_shndx_index as usize - 1]; + buffer.resize(out_section.offset as usize); + buffer.write_bytes(&symtab_shndx_data); + } + if strtab_index != 0 { + let out_section = &out_sections[strtab_index as usize - 1]; + buffer.resize(out_section.offset as usize); + buffer.write_bytes(&strtab_data); } - writer.write_symtab_shndx(); - writer.write_strtab(); // Write non-alloc relocations. - for section in &self.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; } let SectionData::Relocation(relocations) = §ion.data else { continue; }; - writer.write_align_relocation(); + buffer.resize(out_section.offset as usize); for rel in relocations { let r_sym = if let Some(id) = rel.symbol { - out_syms_index[id.0].unwrap().0 + out_syms_index[id.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, - }, - ); + let rel = write::elf::Rel { + r_offset: rel.r_offset, + r_sym, + r_type: rel.r_type, + r_addend: rel.r_addend, + }; + encoder.relocation(buffer, section.sh_type == elf::SHT_RELA, &rel); } } - writer.write_shstrtab(); + if shstrtab_index != 0 { + let out_section = &out_sections[shstrtab_index as usize - 1]; + buffer.resize(out_section.offset as usize); + buffer.write_bytes(&shstrtab_data); + } - writer.write_null_section_header(); + if e_shoff != 0 { + buffer.resize(e_shoff as usize); + encoder.null_section_header(buffer, &layout); + } for out_section in &out_sections { let section = self.sections.get(out_section.id); - match §ion.data { + let mut header = match §ion.data { SectionData::Data(_) | SectionData::UninitializedData(_) | SectionData::Relocation(_) @@ -1628,107 +1738,73 @@ impl<'data> Builder<'data> { | SectionData::Note(_) | SectionData::Dynamic(_) | SectionData::Attributes(_) => { - let sh_size = match §ion.data { - SectionData::Data(data) => data.len() as u64, - SectionData::UninitializedData(len) => *len, - SectionData::Relocation(relocations) => { - (relocations.len() - * self.class().rel_size(section.sh_type == elf::SHT_RELA)) - as u64 - } - SectionData::DynamicRelocation(relocations) => { - (relocations.len() - * self.class().rel_size(section.sh_type == elf::SHT_RELA)) - as u64 - } - SectionData::Note(data) => data.len() as u64, - SectionData::Dynamic(dynamics) => { - ((1 + dynamics.len()) * self.class().dyn_size()) as u64 - } - SectionData::Attributes(_) => out_section.attributes.len() as u64, - _ => { - return Err(Error(format!( - "Unimplemented size for section type {:x}", - section.sh_type - ))); - } - }; let sh_link = if let Some(id) = section.sh_link_section { - if let Some(index) = out_sections_index[id.0] { - index.0 - } else { + let index = out_sections_index[id.0]; + if index == 0 { return Err(Error(format!( "Invalid sh_link from section '{}' to deleted section '{}'", section.name, self.sections.get(id).name, ))); } + index } else { 0 }; let sh_info = if let Some(id) = section.sh_info_section { - if let Some(index) = out_sections_index[id.0] { - index.0 - } else { + let index = out_sections_index[id.0]; + if index == 0 { return Err(Error(format!( "Invalid sh_info link from section '{}' to deleted section '{}'", section.name, self.sections.get(id).name, ))); } + index } else { section.sh_info }; - writer.write_section_header(&write::elf::SectionHeader { - name: out_section.name, + write::elf::SectionHeader { sh_type: section.sh_type, sh_flags: section.sh_flags, - sh_addr: section.sh_addr, - sh_offset: out_section.offset, - sh_size, sh_link, sh_info, sh_addralign: section.sh_addralign, sh_entsize: section.sh_entsize, - }); - } - SectionData::SectionString => { - writer.write_shstrtab_section_header(); + ..Default::default() + } } + SectionData::SectionString => encoder.strtab_section_header(), SectionData::Symbol => { - writer.write_symtab_section_header(1 + num_local as u32); + encoder.symtab_section_header(strtab_index, 1 + num_local as u32) } SectionData::SymbolSectionIndex => { - writer.write_symtab_shndx_section_header(); - } - SectionData::String => { - writer.write_strtab_section_header(); - } - SectionData::DynamicString => { - writer.write_dynstr_section_header(section.sh_addr); + encoder.symtab_shndx_section_header(symtab_index) } + SectionData::String => encoder.strtab_section_header(), + SectionData::DynamicString => encoder.dynstr_section_header(), SectionData::DynamicSymbol => { - writer - .write_dynsym_section_header(section.sh_addr, 1 + num_local_dynamic as u32); - } - SectionData::Hash => { - writer.write_hash_section_header(section.sh_addr); - } - SectionData::GnuHash => { - writer.write_gnu_hash_section_header(section.sh_addr); - } - SectionData::GnuVersym => { - writer.write_gnu_versym_section_header(section.sh_addr); + encoder.dynsym_section_header(dynstr_index, 1 + num_local_dynamic as u32) } + SectionData::Hash => encoder.hash_section_header(dynsym_index), + SectionData::GnuHash => encoder.gnu_hash_section_header(dynsym_index), + SectionData::GnuVersym => encoder.gnu_versym_section_header(dynsym_index), SectionData::GnuVerdef => { - writer.write_gnu_verdef_section_header(section.sh_addr); + encoder.gnu_verdef_section_header(dynstr_index, verdef_count as u32) } SectionData::GnuVerneed => { - writer.write_gnu_verneed_section_header(section.sh_addr); + encoder.gnu_verneed_section_header(dynstr_index, verneed_count as u32) } + }; + header.sh_name = shstrtab.maybe_get_offset(out_section.name); + header.sh_offset = out_section.offset; + header.sh_size = out_section.size; + if header.sh_flags.contains(elf::SHF_ALLOC) { + header.sh_addr = section.sh_addr; } + encoder.section_header(buffer, &header); } - debug_assert_eq!(writer.reserved_len(), writer.len()); + debug_assert_eq!(offset.0, buffer.len() as u64); Ok(()) } @@ -1883,21 +1959,21 @@ impl<'data> Builder<'data> { } } - /// Return the ELF file class that will be written. + /// Return the ELF file encoder. /// /// This can be useful for calculating sizes. - pub fn class(&self) -> write::elf::Class { - write::elf::Class { is_64: self.is_64 } + pub fn encoder(&self) -> write::elf::Encoder { + write::elf::Encoder::new(self.endian, self.is_64, self.header.e_machine) } /// Calculate the size of the file header. pub fn file_header_size(&self) -> usize { - self.class().file_header_size() + self.encoder().file_header_size() } /// Calculate the size of the program headers. pub fn program_headers_size(&self) -> usize { - self.segments.count() * self.class().program_header_size() + self.segments.count() * self.encoder().program_header_size() } /// Calculate the size of the dynamic symbol table. @@ -1905,7 +1981,7 @@ impl<'data> Builder<'data> { /// To get an accurate result, you may need to first call /// [`Self::delete_orphan_symbols`]. pub fn dynamic_symbol_size(&self) -> usize { - (1 + self.dynamic_symbols.count()) * self.class().sym_size() + (1 + self.dynamic_symbols.count()) * self.encoder().sym_size() } /// Calculate the size of the dynamic string table. @@ -1956,7 +2032,7 @@ impl<'data> Builder<'data> { /// [`Self::delete_orphan_symbols`]. pub fn hash_size(&self) -> usize { let chain_count = 1 + self.dynamic_symbols.count(); - self.class() + self.encoder() .hash_size(self.hash_bucket_count, chain_count as u32) } @@ -1966,7 +2042,7 @@ impl<'data> Builder<'data> { /// [`Self::delete_orphan_symbols`]. pub fn gnu_hash_size(&self) -> usize { let symbol_count = self.dynamic_symbols.count_defined(); - self.class().gnu_hash_size( + self.encoder().gnu_hash_size( self.gnu_hash_bloom_count, self.gnu_hash_bucket_count, symbol_count as u32, @@ -1979,7 +2055,7 @@ impl<'data> Builder<'data> { /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. pub fn gnu_versym_size(&self) -> usize { let symbol_count = 1 + self.dynamic_symbols.count(); - self.class().gnu_versym_size(symbol_count) + self.encoder().gnu_versym_size(symbol_count) } /// Calculate the size of the GNU version definition section. @@ -2001,7 +2077,7 @@ impl<'data> Builder<'data> { verdef_count += 1; } } - self.class().gnu_verdef_size(verdef_count, verdaux_count) + self.encoder().gnu_verdef_size(verdef_count, verdaux_count) } /// Calculate the size of the GNU version dependency section. @@ -2016,7 +2092,8 @@ impl<'data> Builder<'data> { vernaux_count += 1; } } - self.class().gnu_verneed_size(verneed_count, vernaux_count) + self.encoder() + .gnu_verneed_size(verneed_count, vernaux_count) } /// Calculate the memory size of a section. @@ -2033,13 +2110,13 @@ impl<'data> Builder<'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) + relocations.len() * self.encoder().rel_size(section.sh_type == elf::SHT_RELA) } SectionData::DynamicRelocation(relocations) => { - relocations.len() * self.class().rel_size(section.sh_type == elf::SHT_RELA) + relocations.len() * self.encoder().rel_size(section.sh_type == elf::SHT_RELA) } SectionData::Note(data) => data.len(), - SectionData::Dynamic(dynamics) => (1 + dynamics.len()) * self.class().dyn_size(), + SectionData::Dynamic(dynamics) => (1 + dynamics.len()) * self.encoder().dyn_size(), SectionData::DynamicString => self.dynamic_string_size(), SectionData::DynamicSymbol => self.dynamic_symbol_size(), SectionData::Hash => self.hash_size(), diff --git a/src/write/elf/encoder.rs b/src/write/elf/encoder.rs new file mode 100644 index 0000000..2df6896 --- /dev/null +++ b/src/write/elf/encoder.rs @@ -0,0 +1,1133 @@ +use alloc::string::String; +use core::mem; + +use crate::Wrap; +use crate::elf; +use crate::endian::*; +use crate::write::util::{write_align, write_pod, write_pod_slice}; +use crate::write::{Error, Result, WritableBuffer}; + +/// Alignment for .symtab_shndx. +pub const ALIGN_SYMTAB_SHNDX: u8 = 4; +/// Alignment for .hash +pub const ALIGN_HASH: u8 = 4; +/// Alignment for .gnu.version +pub const ALIGN_GNU_VERSYM: u8 = 2; +/// Alignment for .gnu.version_d +pub const ALIGN_GNU_VERDEF: u8 = 4; +/// Alignment for .gnu.version_r +pub const ALIGN_GNU_VERNEED: u8 = 4; + +/// Native endian version of [`elf::FileHeader64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone, Default)] +pub struct FileHeader { + pub os_abi: elf::OsAbi, + pub abi_version: u8, + pub e_type: elf::FileType, + pub e_machine: elf::Machine, + pub e_entry: u64, + pub e_flags: elf::FileFlags, +} + +/// Native endian layout-related fields of [`elf::FileHeader64`]. +#[derive(Debug, Clone, Default)] +pub struct FileHeaderLayout { + /// The file offset of the program header table. + pub e_phoff: u64, + /// The number of program headers. + /// + /// Written to `e_phnum`. Overflow is handled during write. + pub segment_num: u32, + /// The file offset of the section header table. + pub e_shoff: u64, + /// The number of section headers. + /// + /// Written to `e_shnum`. Overflow is handled during write. + pub section_num: u32, + /// The section header string table index. + /// + /// Written to `e_shstrndx`. Overflow is handled during write. + pub shstrtab_index: u32, +} + +/// Native endian version of [`elf::ProgramHeader64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct ProgramHeader { + pub p_type: elf::ProgramType, + pub p_flags: elf::ProgramFlags, + pub p_offset: u64, + pub p_vaddr: u64, + pub p_paddr: u64, + pub p_filesz: u64, + pub p_memsz: u64, + pub p_align: u64, +} + +/// Native endian version of [`elf::SectionHeader64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone, Default)] +pub struct SectionHeader { + /// The string table offset of the section name, or 0. + pub sh_name: u32, + pub sh_type: elf::SectionType, + pub sh_flags: elf::SectionFlags, + pub sh_addr: u64, + pub sh_offset: u64, + pub sh_size: u64, + pub sh_link: u32, + pub sh_info: u32, + pub sh_addralign: u64, + pub sh_entsize: u64, +} + +/// Native endian version of [`elf::Sym64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Sym { + /// The section index for the `st_shndx` field. + /// + /// Overflow is handled during write. + /// For special values, set this to `None` and set `st_shndx` instead. + pub section: Option, + pub st_name: u32, + pub st_info: elf::SymbolInfo, + pub st_other: elf::SymbolOther, + pub st_shndx: elf::SymbolSection, + pub st_value: u64, + pub st_size: u64, +} + +/// Unified native endian version of [`elf::Rel64`] and [`elf::Rela64`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Rel { + pub r_offset: u64, + pub r_sym: u32, + pub r_type: u32, + pub r_addend: i64, +} + +/// Information required for writing [`elf::GnuHashHeader`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct GnuHashTable { + pub bucket_count: u32, + pub bloom_count: u32, + pub bloom_shift: u32, + pub symbol_base: u32, + pub symbol_count: u32, +} + +/// Information required for writing [`elf::Verdef`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Verdef { + pub version: u16, + pub flags: elf::VersionFlags, + pub index: elf::VersionIndex, + pub aux_count: u16, + /// The string table offset of the name for the first [`elf::Verdaux`] entry. + /// + /// Written to the `vda_name` field of the first verdaux. + pub name: u32, + /// The hash of the name for the first [`elf::Verdaux`] entry, as computed by + /// [`elf::hash`]. Written to `vd_hash`. + pub hash: u32, +} + +/// Information required for writing [`elf::Verneed`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Verneed { + pub version: u16, + pub aux_count: u16, + /// The string table offset of the file name. Written to `vn_file`. + pub file: u32, +} + +/// Information required for writing [`elf::Vernaux`]. +#[allow(missing_docs)] +#[derive(Debug, Clone)] +pub struct Vernaux { + pub flags: elf::VersionFlags, + pub index: elf::VersionIndex, + /// The string table offset of the version name. Written to `vna_name`. + pub name: u32, + /// The hash of the version name, as computed by [`elf::hash`]. Written to `vna_hash`. + pub hash: u32, +} + +/// A helper for encoding headers and data when writing an ELF file. +#[derive(Debug, Clone, Copy)] +pub struct Encoder { + endian: E, + is_64: bool, + is_mips64el: bool, +} + +impl Encoder { + /// Create a new `Encoder` for the given endianness and ELF class, and machine. + /// + /// `machine` is required to detect mips64el. If it is unknown at construction, + /// it may be specified as `EM_NONE` here, and set later via `set_machine`. + pub fn new(endian: E, is_64: bool, machine: elf::Machine) -> Self { + let mut encoder = Encoder { + endian, + is_64, + is_mips64el: false, + }; + encoder.set_machine(machine); + encoder + } + + /// Set the machine. + /// + /// This is required to support the encoding of mips64el relocations. + pub fn set_machine(&mut self, machine: elf::Machine) { + self.is_mips64el = self.is_64 && self.endian.is_little_endian() && machine == elf::EM_MIPS; + } + + /// Return the endianness. + pub fn endian(self) -> E { + self.endian + } + + /// Return true for 64-bit ELF. + pub fn is_64(self) -> bool { + self.is_64 + } + + /// Return true for little-endian 64-bit MIPS. + pub fn is_mips64el(self) -> bool { + self.is_mips64el + } + + /// Return the size in bytes of an address for the ELF class. + pub fn address_size(self) -> u8 { + if self.is_64 { 8 } else { 4 } + } + + /// Write alignment padding bytes corresponding to the ELF class. + /// + /// Required for: program headers, section headers, symbols, dynamics, relocations, + /// and .gnu.hash. + /// + /// Returns the file offset after the padding. + pub fn address_align(self, buffer: &mut W) -> u64 { + write_align(buffer, self.address_size() as usize); + buffer.len() as u64 + } + + /// Return the size of the file header. + pub fn file_header_size(self) -> usize { + if self.is_64 { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + + /// Write the file header. + /// + /// The buffer should be at the start of the file. + pub fn file_header( + self, + buffer: &mut W, + header: &FileHeader, + layout: &FileHeaderLayout, + ) -> Result<()> { + let endian = self.endian; + let e_ident = elf::Ident { + magic: elf::ELFMAG, + class: if self.is_64 { + elf::ELFCLASS64 + } else { + elf::ELFCLASS32 + }, + data: if self.endian.is_little_endian() { + elf::ELFDATA2LSB + } else { + elf::ELFDATA2MSB + }, + version: elf::EV_CURRENT, + os_abi: header.os_abi, + abi_version: header.abi_version, + padding: [0; 7], + }; + + let e_ehsize = self.file_header_size() as u16; + + let e_phentsize = if layout.segment_num == 0 { + 0 + } else { + self.program_header_size() as u16 + }; + let e_phnum = if layout.segment_num >= elf::PN_XNUM.into() { + if layout.section_num == 0 { + return Err(Error(String::from( + "e_phnum overflow requires section headers", + ))); + } + elf::PN_XNUM + } else { + layout.segment_num as u16 + }; + + let e_shentsize = if layout.section_num == 0 { + 0 + } else { + self.section_header_size() as u16 + }; + let e_shnum = if layout.section_num >= elf::SHN_LORESERVE.into() { + 0 + } else { + layout.section_num as u16 + }; + let e_shstrndx = elf::SymbolSection::new(layout.shstrtab_index); + + if self.is_64 { + let data = &elf::FileHeader64 { + e_ident, + e_type: U16::new(endian, header.e_type), + e_machine: U16::new(endian, header.e_machine), + e_version: U32::new(endian, elf::EV_CURRENT.0.into()), + e_entry: U64::new(endian, header.e_entry), + e_phoff: U64::new(endian, layout.e_phoff), + e_shoff: U64::new(endian, layout.e_shoff), + e_flags: U32::new(endian, header.e_flags), + e_ehsize: U16::new(endian, e_ehsize), + e_phentsize: U16::new(endian, e_phentsize), + e_phnum: U16::new(endian, e_phnum), + e_shentsize: U16::new(endian, e_shentsize), + e_shnum: U16::new(endian, e_shnum), + e_shstrndx: U16::new(endian, e_shstrndx), + }; + write_pod(buffer, data); + } else { + let data = &elf::FileHeader32 { + e_ident, + e_type: U16::new(endian, header.e_type), + e_machine: U16::new(endian, header.e_machine), + e_version: U32::new(endian, elf::EV_CURRENT.0.into()), + e_entry: U32::new(endian, header.e_entry as u32), + e_phoff: U32::new(endian, layout.e_phoff as u32), + e_shoff: U32::new(endian, layout.e_shoff as u32), + e_flags: U32::new(endian, header.e_flags), + e_ehsize: U16::new(endian, e_ehsize), + e_phentsize: U16::new(endian, e_phentsize), + e_phnum: U16::new(endian, e_phnum), + e_shentsize: U16::new(endian, e_shentsize), + e_shnum: U16::new(endian, e_shnum), + e_shstrndx: U16::new(endian, e_shstrndx), + }; + write_pod(buffer, data); + } + + Ok(()) + } + + /// Return the size of a program header. + pub fn program_header_size(self) -> usize { + if self.is_64 { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + + /// Write a program header. + /// + /// The buffer should already be aligned to `address_size`. + pub fn program_header( + self, + buffer: &mut W, + header: &ProgramHeader, + ) { + let endian = self.endian; + if self.is_64 { + let data = &elf::ProgramHeader64 { + p_type: U32::new(endian, header.p_type), + p_flags: U32::new(endian, header.p_flags), + p_offset: U64::new(endian, header.p_offset), + p_vaddr: U64::new(endian, header.p_vaddr), + p_paddr: U64::new(endian, header.p_paddr), + p_filesz: U64::new(endian, header.p_filesz), + p_memsz: U64::new(endian, header.p_memsz), + p_align: U64::new(endian, header.p_align), + }; + write_pod(buffer, data); + } else { + let data = &elf::ProgramHeader32 { + p_type: U32::new(endian, header.p_type), + p_offset: U32::new(endian, header.p_offset as u32), + p_vaddr: U32::new(endian, header.p_vaddr as u32), + p_paddr: U32::new(endian, header.p_paddr as u32), + p_filesz: U32::new(endian, header.p_filesz as u32), + p_memsz: U32::new(endian, header.p_memsz as u32), + p_flags: U32::new(endian, header.p_flags), + p_align: U32::new(endian, header.p_align as u32), + }; + write_pod(buffer, data); + } + } + + /// Return the size of a section header. + pub fn section_header_size(self) -> usize { + if self.is_64 { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + + /// Write the section header at index 0. + /// + /// The layout is used to set fields if file header values overflowed. + /// + /// The buffer should already be aligned to `address_size`. + pub fn null_section_header( + self, + buffer: &mut W, + layout: &FileHeaderLayout, + ) { + let sh_size = if layout.section_num >= elf::SHN_LORESERVE.into() { + layout.section_num + } else { + 0 + }; + let sh_link = if layout.shstrtab_index >= elf::SHN_LORESERVE.into() { + layout.shstrtab_index + } else { + 0 + }; + let sh_info = if layout.segment_num >= elf::PN_XNUM.into() { + layout.segment_num + } else { + 0 + }; + let endian = self.endian; + if self.is_64 { + let data = &elf::SectionHeader64 { + sh_name: U32::new(endian, 0), + sh_type: U32::new(endian, elf::SHT_NULL), + sh_flags: U64::new(endian, elf::SectionFlags(0)), + sh_addr: U64::new(endian, 0), + sh_offset: U64::new(endian, 0), + sh_size: U64::new(endian, sh_size.into()), + sh_link: U32::new(endian, sh_link), + sh_info: U32::new(endian, sh_info), + sh_addralign: U64::new(endian, 0), + sh_entsize: U64::new(endian, 0), + }; + write_pod(buffer, data); + } else { + let data = &elf::SectionHeader32 { + sh_name: U32::new(endian, 0), + sh_type: U32::new(endian, elf::SHT_NULL), + sh_flags: U32::new_u64_truncate(endian, elf::SectionFlags(0)), + sh_addr: U32::new(endian, 0), + sh_offset: U32::new(endian, 0), + sh_size: U32::new(endian, sh_size), + sh_link: U32::new(endian, sh_link), + sh_info: U32::new(endian, sh_info), + sh_addralign: U32::new(endian, 0), + sh_entsize: U32::new(endian, 0), + }; + write_pod(buffer, data); + } + } + + /// Write a section header. + /// + /// The buffer should already be aligned to `address_size`. + pub fn section_header( + self, + buffer: &mut W, + section: &SectionHeader, + ) { + let endian = self.endian; + if self.is_64 { + let data = &elf::SectionHeader64 { + sh_name: U32::new(endian, section.sh_name), + sh_type: U32::new(endian, section.sh_type), + sh_flags: U64::new(endian, section.sh_flags), + sh_addr: U64::new(endian, section.sh_addr), + sh_offset: U64::new(endian, section.sh_offset), + sh_size: U64::new(endian, section.sh_size), + sh_link: U32::new(endian, section.sh_link), + sh_info: U32::new(endian, section.sh_info), + sh_addralign: U64::new(endian, section.sh_addralign), + sh_entsize: U64::new(endian, section.sh_entsize), + }; + write_pod(buffer, data); + } else { + let data = &elf::SectionHeader32 { + sh_name: U32::new(endian, section.sh_name), + sh_type: U32::new(endian, section.sh_type), + sh_flags: U32::new_u64_truncate(endian, section.sh_flags), + sh_addr: U32::new(endian, section.sh_addr as u32), + sh_offset: U32::new(endian, section.sh_offset as u32), + sh_size: U32::new(endian, section.sh_size as u32), + sh_link: U32::new(endian, section.sh_link), + sh_info: U32::new(endian, section.sh_info), + sh_addralign: U32::new(endian, section.sh_addralign as u32), + sh_entsize: U32::new(endian, section.sh_entsize as u32), + }; + write_pod(buffer, data); + } + } + + /// Return a section header for `elf::SHT_STRTAB`. + /// + /// The caller must set `sh_name`, `sh_offset`, and `sh_size`. + pub fn strtab_section_header(self) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_STRTAB, + sh_addralign: 1, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_STRTAB` with `elf::SHF_ALLOC`. + /// + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn dynstr_section_header(self) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_STRTAB, + sh_flags: elf::SHF_ALLOC, + sh_addralign: 1, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_SYMTAB`. + /// + /// `sh_link` is set to `strtab`. `sh_info` is set to `num_local`. + /// The caller must set `sh_name`, `sh_offset`, and `sh_size`. + pub fn symtab_section_header(self, strtab: u32, num_local: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_SYMTAB, + sh_link: strtab, + sh_info: num_local, + sh_addralign: self.address_size().into(), + sh_entsize: self.sym_size() as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_SYMTAB_SHNDX`. + /// + /// `sh_link` is set to `symtab`. + /// The caller must set `sh_name`, `sh_offset`, `sh_size`. + pub fn symtab_shndx_section_header(self, symtab: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_SYMTAB_SHNDX, + sh_link: symtab, + sh_addralign: ALIGN_SYMTAB_SHNDX as u64, + sh_entsize: 4, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_DYNSYM`. + /// + /// `sh_link` is set to `dynstr`. `sh_info` is set to `num_local`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn dynsym_section_header(self, dynstr: u32, num_local: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_DYNSYM, + sh_flags: elf::SHF_ALLOC, + sh_link: dynstr, + sh_info: num_local, + sh_addralign: self.address_size().into(), + sh_entsize: self.sym_size() as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_DYNAMIC`. + /// + /// `sh_link` is set to `dynstr`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn dynamic_section_header(self, dynstr: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_DYNAMIC, + sh_flags: elf::SHF_WRITE | elf::SHF_ALLOC, + sh_link: dynstr, + sh_addralign: self.address_size().into(), + sh_entsize: self.dyn_size() as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_HASH`. + /// + /// `sh_link` is set to `dynsym`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn hash_section_header(self, dynsym: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_HASH, + sh_flags: elf::SHF_ALLOC, + sh_link: dynsym, + sh_addralign: ALIGN_HASH as u64, + sh_entsize: 4, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_GNU_HASH`. + /// + /// `sh_link` is set to `dynsym`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn gnu_hash_section_header(self, dynsym: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_GNU_HASH, + sh_flags: elf::SHF_ALLOC, + sh_link: dynsym, + sh_addralign: self.address_size().into(), + sh_entsize: if self.is_64 { 0 } else { 4 }, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_GNU_VERSYM`. + /// + /// `sh_link` is set to `dynsym`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn gnu_versym_section_header(self, dynsym: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_GNU_VERSYM, + sh_flags: elf::SHF_ALLOC, + sh_link: dynsym, + sh_addralign: ALIGN_GNU_VERSYM as u64, + sh_entsize: 2, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_GNU_VERDEF`. + /// + /// `sh_link` is set to `dynstr`. `sh_info` is set to `verdef_count`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn gnu_verdef_section_header(self, dynstr: u32, verdef_count: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_GNU_VERDEF, + sh_flags: elf::SHF_ALLOC, + sh_link: dynstr, + sh_info: verdef_count, + sh_addralign: ALIGN_GNU_VERDEF as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_GNU_VERNEED`. + /// + /// `sh_link` is set to `dynstr`. `sh_info` is set to `verneed_count`. + /// The caller must set `sh_name`, `sh_addr`, `sh_offset`, and `sh_size`. + pub fn gnu_verneed_section_header(self, dynstr: u32, verneed_count: u32) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_GNU_VERNEED, + sh_flags: elf::SHF_ALLOC, + sh_link: dynstr, + sh_info: verneed_count, + sh_addralign: ALIGN_GNU_VERNEED as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for `elf::SHT_GNU_ATTRIBUTES`. + /// + /// The caller must set `sh_name`, `sh_offset`, `sh_size`, and `sh_link`. + pub fn gnu_attributes_section_header(self) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_GNU_ATTRIBUTES, + sh_addralign: self.address_size().into(), + ..SectionHeader::default() + } + } + + /// Return a section header for a relocation section. + /// + /// `is_rela` determines `sh_type` (`elf::SHT_RELA` or `elf::SHT_REL`) and + /// `sh_entsize`. The caller must set `sh_name`, `sh_offset`, `sh_size`, + /// `sh_link` (the symbol table section index), and `sh_info` (the section + /// index the relocations apply to). + pub fn relocation_section_header(self, is_rela: bool) -> SectionHeader { + SectionHeader { + sh_type: if is_rela { elf::SHT_RELA } else { elf::SHT_REL }, + sh_flags: elf::SHF_INFO_LINK, + sh_addralign: self.address_size().into(), + sh_entsize: self.rel_size(is_rela) as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for a relative relocation section. + /// + /// The caller must set `sh_name`, `sh_offset`, and `sh_size`. + pub fn relative_relocation_section_header(self) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_RELA, + sh_addralign: self.address_size().into(), + sh_entsize: self.relr_size() as u64, + ..SectionHeader::default() + } + } + + /// Return a section header for a COMDAT group section (`elf::SHT_GROUP`). + /// + /// The caller must set `sh_name`, `sh_offset`, `sh_size`, `sh_link` (the + /// symbol table section index), and `sh_info` (the group signature symbol + /// index). + pub fn comdat_section_header(self) -> SectionHeader { + SectionHeader { + sh_type: elf::SHT_GROUP, + sh_addralign: 4, + sh_entsize: 4, + ..SectionHeader::default() + } + } + + /// Return the size of a symbol. + pub fn sym_size(self) -> usize { + if self.is_64 { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + + /// Write the null symbol. + /// + /// The buffer should already be aligned to `address_size`. + pub fn null_symbol(self, buffer: &mut W) { + if self.is_64 { + write_pod(buffer, &elf::Sym64::::default()); + } else { + write_pod(buffer, &elf::Sym32::::default()); + } + } + + /// Write a symbol. + /// + /// Returns the extended symbol index if overflow occurred. + /// + /// The buffer should already be aligned to `address_size`. + pub fn symbol(self, buffer: &mut W, sym: &Sym) -> Option { + let st_shndx = if let Some(section) = sym.section { + elf::SymbolSection::new(section) + } else { + sym.st_shndx + }; + + let endian = self.endian; + if self.is_64 { + let data = &elf::Sym64 { + st_name: U32::new(endian, sym.st_name), + st_info: sym.st_info, + st_other: sym.st_other, + st_shndx: U16::new(endian, st_shndx), + st_value: U64::new(endian, sym.st_value), + st_size: U64::new(endian, sym.st_size), + }; + write_pod(buffer, data); + } else { + let data = &elf::Sym32 { + st_name: U32::new(endian, sym.st_name), + st_info: sym.st_info, + st_other: sym.st_other, + st_shndx: U16::new(endian, st_shndx), + st_value: U32::new(endian, sym.st_value as u32), + st_size: U32::new(endian, sym.st_size as u32), + }; + write_pod(buffer, data); + } + + if st_shndx == elf::SHN_XINDEX { + Some(sym.section.unwrap_or(0)) + } else { + None + } + } + + /// Write a `u32` value. + /// + /// Useful for .symtab_shndx or COMDAT section groups. + pub(crate) fn u32 + Copy + 'static>( + self, + buffer: &mut W, + value: T, + ) { + write_pod(buffer, &U32::new(self.endian, value)); + } + + /// Return the size of a relocation entry. + pub fn rel_size(self, is_rela: bool) -> usize { + if self.is_64 { + if is_rela { + mem::size_of::>() + } else { + mem::size_of::>() + } + } else { + if is_rela { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + } + + /// Return the size of a relative relocation entry. + pub fn relr_size(self) -> usize { + if self.is_64 { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + + /// Write a relocation. + /// + /// The buffer should already be aligned to `address_size`. + pub fn relocation(self, buffer: &mut W, is_rela: bool, rel: &Rel) { + let endian = self.endian; + if self.is_64 { + if is_rela { + let data = &elf::Rela64 { + r_offset: U64::new(endian, rel.r_offset), + r_info: elf::Rela64::r_info(endian, self.is_mips64el, rel.r_sym, rel.r_type), + r_addend: I64::new(endian, rel.r_addend), + }; + write_pod(buffer, data); + } else { + let data = &elf::Rel64 { + r_offset: U64::new(endian, rel.r_offset), + r_info: elf::Rel64::r_info(endian, rel.r_sym, rel.r_type), + }; + write_pod(buffer, data); + } + } else { + if is_rela { + let data = &elf::Rela32 { + r_offset: U32::new(endian, rel.r_offset as u32), + r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), + r_addend: I32::new(endian, rel.r_addend as i32), + }; + write_pod(buffer, data); + } else { + let data = &elf::Rel32 { + r_offset: U32::new(endian, rel.r_offset as u32), + r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), + }; + write_pod(buffer, data); + } + } + } + + /// Return the size of a dynamic entry. + pub fn dyn_size(self) -> usize { + if self.is_64 { + mem::size_of::>() + } else { + mem::size_of::>() + } + } + + /// Write a dynamic value entry. + /// + /// Returns an error for 32-bit ELF overflows. + /// + /// The buffer should already be aligned to `address_size`. + pub fn dynamic( + self, + buffer: &mut W, + d_tag: elf::DynamicTag, + d_val: u64, + ) -> Result<()> { + let endian = self.endian; + if self.is_64 { + let data = &elf::Dyn64 { + d_tag: I64::new(endian, d_tag), + d_val: U64::new(endian, d_val), + }; + write_pod(buffer, data); + } else { + let d_tag = I32::new_i64(endian, d_tag) + .map_err(|_| Error(format!("d_tag overflow: 0x{:x}", d_tag)))?; + let d_val = d_val + .try_into() + .map_err(|_| Error(format!("d_val overflow: 0x{:x}", d_val)))?; + let data = &elf::Dyn32 { + d_tag, + d_val: U32::new(endian, d_val), + }; + write_pod(buffer, data); + } + Ok(()) + } + + /// Return the size of a hash table. + pub fn hash_size(self, bucket_count: u32, chain_count: u32) -> usize { + mem::size_of::>() + + bucket_count as usize * 4 + + chain_count as usize * 4 + } + + /// Write a SysV hash table. + /// + /// `chain_count` is the number of symbols in the hash. + /// The argument to `hash` will be in the range `0..chain_count`. + /// + /// The buffer should already be aligned to 4. + pub fn hash_table(self, buffer: &mut W, bucket_count: u32, chain_count: u32, hash: F) + where + W: WritableBuffer + ?Sized, + F: Fn(u32) -> Option, + { + let mut buckets = vec![U32::new(self.endian, 0); bucket_count as usize]; + let mut chains = vec![U32::new(self.endian, 0); chain_count as usize]; + for i in 0..chain_count { + if let Some(hash) = hash(i) { + let bucket = hash % bucket_count; + chains[i as usize] = buckets[bucket as usize]; + buckets[bucket as usize] = U32::new(self.endian, i); + } + } + + let data = &elf::HashHeader { + bucket_count: U32::new(self.endian, bucket_count), + chain_count: U32::new(self.endian, chain_count), + }; + write_pod(buffer, data); + write_pod_slice(buffer, &buckets); + write_pod_slice(buffer, &chains); + } + + /// Return the size of a GNU hash table. + pub fn gnu_hash_size(self, bloom_count: u32, bucket_count: u32, symbol_count: u32) -> usize { + let bloom_size = if self.is_64 { 8 } else { 4 }; + mem::size_of::>() + + bloom_count as usize * bloom_size + + bucket_count as usize * 4 + + symbol_count as usize * 4 + } + + /// Write a GNU hash section. + /// + /// `symbol_count` is the number of symbols in the hash. + /// The argument to `hash` will be in the range `0..symbol_count`. + /// + /// This requires that symbols are already sorted by bucket. + pub fn gnu_hash_table(self, buffer: &mut W, table: &GnuHashTable, hash: F) + where + W: WritableBuffer + ?Sized, + F: Fn(u32) -> u32, + { + let GnuHashTable { + bucket_count, + bloom_count, + bloom_shift, + symbol_base, + symbol_count, + } = *table; + + let data = &elf::GnuHashHeader { + bucket_count: U32::new(self.endian, bucket_count), + symbol_base: U32::new(self.endian, symbol_base), + bloom_count: U32::new(self.endian, bloom_count), + bloom_shift: U32::new(self.endian, bloom_shift), + }; + write_pod(buffer, data); + + // Calculate and write bloom filter. + if self.is_64 { + let mut bloom_filters = vec![0u64; bloom_count as usize]; + for i in 0..symbol_count { + let h = hash(i); + bloom_filters[((h / 64) & (bloom_count - 1)) as usize] |= + 1 << (h % 64) | 1 << ((h >> bloom_shift) % 64); + } + for bloom_filter in bloom_filters { + write_pod(buffer, &U64::new(self.endian, bloom_filter)); + } + } else { + let mut bloom_filters = vec![0u32; bloom_count as usize]; + for i in 0..symbol_count { + let h = hash(i); + bloom_filters[((h / 32) & (bloom_count - 1)) as usize] |= + 1 << (h % 32) | 1 << ((h >> bloom_shift) % 32); + } + for bloom_filter in bloom_filters { + write_pod(buffer, &U32::new(self.endian, bloom_filter)); + } + } + + // Write buckets. + // + // This requires that symbols are already sorted by bucket. + let mut bucket = 0; + for i in 0..symbol_count { + let symbol_bucket = hash(i) % bucket_count; + while bucket < symbol_bucket { + write_pod(buffer, &U32::new(self.endian, 0u32)); + bucket += 1; + } + if bucket == symbol_bucket { + write_pod(buffer, &U32::new(self.endian, symbol_base + i)); + bucket += 1; + } + } + while bucket < bucket_count { + write_pod(buffer, &U32::new(self.endian, 0u32)); + bucket += 1; + } + + // Write hash values. + for i in 0..symbol_count { + let mut h = hash(i); + if i == symbol_count - 1 || h % bucket_count != hash(i + 1) % bucket_count { + h |= 1; + } else { + h &= !1; + } + write_pod(buffer, &U32::new(self.endian, h)); + } + } + + /// Return the size of a GNU symbol version section. + pub fn gnu_versym_size(self, symbol_count: usize) -> usize { + symbol_count * 2 + } + + /// Write a symbol version entry. + pub fn gnu_versym(self, buffer: &mut W, versym: elf::VersymIndex) { + write_pod(buffer, &U16::new(self.endian, versym)); + } + + /// Return the size of a GNU version definition section. + pub fn gnu_verdef_size(self, verdef_count: usize, verdaux_count: usize) -> usize { + verdef_count * mem::size_of::>() + + verdaux_count * mem::size_of::>() + } + + /// Write a version definition entry. + pub fn gnu_verdef( + self, + buffer: &mut W, + next: bool, + verdef: &Verdef, + ) { + let vd_next = if next { + mem::size_of::>() as u32 + + verdef.aux_count as u32 * mem::size_of::>() as u32 + } else { + 0 + }; + let vd_aux = mem::size_of::>() as u32; + let data = &elf::Verdef { + vd_version: U16::new(self.endian, verdef.version), + vd_flags: U16::new(self.endian, verdef.flags), + vd_ndx: U16::new(self.endian, verdef.index), + vd_cnt: U16::new(self.endian, verdef.aux_count), + vd_hash: U32::new(self.endian, verdef.hash), + vd_aux: U32::new(self.endian, vd_aux), + vd_next: U32::new(self.endian, vd_next), + }; + write_pod(buffer, data); + self.gnu_verdaux(buffer, verdef.aux_count > 1, verdef.name); + } + + /// Write a version definition entry that shares the names of the next definition. + /// + /// This is typically useful when there are only two versions (including the base) + /// and they have the same name. + pub fn gnu_verdef_shared(self, buffer: &mut W, verdef: &Verdef) { + let vd_next = mem::size_of::>() as u32; + let vd_aux = 2 * mem::size_of::>() as u32; + let data = &elf::Verdef { + vd_version: U16::new(self.endian, verdef.version), + vd_flags: U16::new(self.endian, verdef.flags), + vd_ndx: U16::new(self.endian, verdef.index), + vd_cnt: U16::new(self.endian, verdef.aux_count), + vd_hash: U32::new(self.endian, verdef.hash), + vd_aux: U32::new(self.endian, vd_aux), + vd_next: U32::new(self.endian, vd_next), + }; + write_pod(buffer, data); + } + + /// Write a version definition auxiliary entry. + /// + /// `name` is the offset of the name in the dynamic string table. + pub fn gnu_verdaux(self, buffer: &mut W, next: bool, name: u32) { + let vda_next = if next { + mem::size_of::>() as u32 + } else { + 0 + }; + let data = &elf::Verdaux { + vda_name: U32::new(self.endian, name), + vda_next: U32::new(self.endian, vda_next), + }; + write_pod(buffer, data); + } + + /// Return the size of a GNU version dependency section. + pub fn gnu_verneed_size(self, verneed_count: usize, vernaux_count: usize) -> usize { + verneed_count * mem::size_of::>() + + vernaux_count * mem::size_of::>() + } + + /// Write a version needed entry. + pub fn gnu_verneed( + self, + buffer: &mut W, + next: bool, + verneed: &Verneed, + ) { + let vn_next = if next { + mem::size_of::>() as u32 + + verneed.aux_count as u32 * mem::size_of::>() as u32 + } else { + 0 + }; + let vn_aux = if verneed.aux_count != 0 { + mem::size_of::>() as u32 + } else { + 0 + }; + let data = &elf::Verneed { + vn_version: U16::new(self.endian, verneed.version), + vn_cnt: U16::new(self.endian, verneed.aux_count), + vn_file: U32::new(self.endian, verneed.file), + vn_aux: U32::new(self.endian, vn_aux), + vn_next: U32::new(self.endian, vn_next), + }; + write_pod(buffer, data); + } + + /// Write a version needed auxiliary entry. + pub fn gnu_vernaux( + self, + buffer: &mut W, + next: bool, + vernaux: &Vernaux, + ) { + let vna_next = if next { + mem::size_of::>() as u32 + } else { + 0 + }; + let data = &elf::Vernaux { + vna_hash: U32::new(self.endian, vernaux.hash), + vna_flags: U16::new(self.endian, vernaux.flags), + vna_other: U16::new(self.endian, vernaux.index), + vna_name: U32::new(self.endian, vernaux.name), + vna_next: U32::new(self.endian, vna_next), + }; + write_pod(buffer, data); + } +} diff --git a/src/write/elf/mod.rs b/src/write/elf/mod.rs index 3a4f371..529fa0d 100644 --- a/src/write/elf/mod.rs +++ b/src/write/elf/mod.rs @@ -5,5 +5,8 @@ mod object; +mod encoder; +pub use encoder::*; + mod writer; pub use writer::*; diff --git a/src/write/elf/object.rs b/src/write/elf/object.rs index 76a5031..b069906 100644 --- a/src/write/elf/object.rs +++ b/src/write/elf/object.rs @@ -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, diff --git a/src/write/elf/writer.rs b/src/write/elf/writer.rs index 0961b2a..d07f7cb 100644 --- a/src/write/elf/writer.rs +++ b/src/write/elf/writer.rs @@ -1,22 +1,15 @@ //! Helper for writing ELF files. use alloc::string::String; use alloc::vec::Vec; -use core::convert::TryInto; -use core::mem; use crate::elf; use crate::endian::*; use crate::pod; +use crate::write::elf::encoder::*; use crate::write::string::{StringId, StringTable}; use crate::write::util; use crate::write::{Error, GrowableBuffer, Result, WritableBuffer}; -const ALIGN_SYMTAB_SHNDX: usize = 4; -const ALIGN_HASH: usize = 4; -const ALIGN_GNU_VERSYM: usize = 2; -const ALIGN_GNU_VERDEF: usize = 4; -const ALIGN_GNU_VERNEED: usize = 4; - /// The index of an ELF section. #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SectionIndex(pub u32); @@ -43,7 +36,7 @@ mod private { fn need_offset(offset: u64) -> bool; fn set_offset(dest: &mut u64, offset: u64); fn set_size(dest: &mut u64, size: u64); - fn set_section_index(dest: &mut SectionIndex, index: SectionIndex); + fn set_section_index(dest: &mut u32, index: SectionIndex); fn set_section_name( dest: &mut Option, shstrtab: &mut StringTable<'_>, @@ -55,6 +48,9 @@ mod private { /// A helper for writing ELF files. /// +/// This is a stateful layer over [`Encoder`] to keep track over bookkeeping +/// information such as strings, section ranges, section indices, and counts of items. +/// /// The writer supports two modes: [`TwoPhase`] or [`SinglePhase`]. /// See the mode documentation for a description of the use of the writer. /// @@ -63,11 +59,7 @@ mod private { #[allow(missing_debug_implementations)] pub struct Writer<'a, M: Mode = TwoPhase> { mode: M, - endian: Endianness, - is_64: bool, - is_mips64el: bool, - elf_align: usize, - + encoder: Encoder, buffer: &'a mut dyn WritableBuffer, header: FileHeader, @@ -156,15 +148,9 @@ impl<'a, M: Mode> Writer<'a, M> { buffer: &'a mut dyn WritableBuffer, mode: M, ) -> Self { - let elf_align = if is_64 { 8 } else { 4 }; Writer { mode, - endian, - is_64, - // Determined later. - is_mips64el: false, - elf_align, - + encoder: Encoder::new(endian, is_64, elf::EM_NONE), buffer, header: FileHeader::default(), @@ -246,11 +232,6 @@ impl<'a, M: Mode> Writer<'a, M> { } } - /// Get the file class that will be written. - fn class(&self) -> Class { - Class { is_64: self.is_64 } - } - /// Return the current file length that has been written. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { @@ -294,117 +275,11 @@ impl<'a, M: Mode> Writer<'a, M> { pub fn write_file_header(&mut self, header: &FileHeader) -> Result<()> { debug_assert_eq!(self.buffer.len(), 0); - self.is_mips64el = - self.is_64 && self.endian.is_little_endian() && header.e_machine == elf::EM_MIPS; - self.mode.reserve(self.buffer)?; self.header = header.clone(); - Self::write_file_header_impl( - self.buffer, - self.endian, - self.is_64, - &self.header, - &self.layout, - ) - } - - fn write_file_header_impl( - buffer: &mut dyn WritableBuffer, - endian: Endianness, - is_64: bool, - header: &FileHeader, - layout: &FileHeaderLayout, - ) -> Result<()> { - let class = Class { is_64 }; - let e_ident = elf::Ident { - magic: elf::ELFMAG, - class: if class.is_64 { - elf::ELFCLASS64 - } else { - elf::ELFCLASS32 - }, - data: if endian.is_little_endian() { - elf::ELFDATA2LSB - } else { - elf::ELFDATA2MSB - }, - version: elf::EV_CURRENT, - os_abi: header.os_abi, - abi_version: header.abi_version, - padding: [0; 7], - }; - - let e_ehsize = class.file_header_size() as u16; - - let e_phoff = layout.segment_offset; - let e_phentsize = if layout.segment_num == 0 { - 0 - } else { - class.program_header_size() as u16 - }; - let e_phnum = if layout.segment_num >= elf::PN_XNUM.into() { - if layout.section_num == 0 { - return Err(Error(String::from( - "e_phnum overflow requires section headers", - ))); - } - elf::PN_XNUM - } else { - layout.segment_num as u16 - }; - - let e_shoff = layout.section_offset; - let e_shentsize = if layout.section_num == 0 { - 0 - } else { - class.section_header_size() as u16 - }; - let e_shnum = if layout.section_num >= elf::SHN_LORESERVE.into() { - 0 - } else { - layout.section_num as u16 - }; - let e_shstrndx = elf::SymbolSection::new(layout.shstrtab_index.0); - - if class.is_64 { - let file = elf::FileHeader64 { - e_ident, - e_type: U16::new(endian, header.e_type), - e_machine: U16::new(endian, header.e_machine), - e_version: U32::new(endian, elf::EV_CURRENT.0.into()), - e_entry: U64::new(endian, header.e_entry), - e_phoff: U64::new(endian, e_phoff), - e_shoff: U64::new(endian, e_shoff), - e_flags: U32::new(endian, header.e_flags), - e_ehsize: U16::new(endian, e_ehsize), - e_phentsize: U16::new(endian, e_phentsize), - e_phnum: U16::new(endian, e_phnum), - e_shentsize: U16::new(endian, e_shentsize), - e_shnum: U16::new(endian, e_shnum), - e_shstrndx: U16::new(endian, e_shstrndx), - }; - buffer.write(&file) - } else { - let file = elf::FileHeader32 { - e_ident, - e_type: U16::new(endian, header.e_type), - e_machine: U16::new(endian, header.e_machine), - e_version: U32::new(endian, elf::EV_CURRENT.0.into()), - e_entry: U32::new(endian, header.e_entry as u32), - e_phoff: U32::new(endian, e_phoff as u32), - e_shoff: U32::new(endian, e_shoff as u32), - e_flags: U32::new(endian, header.e_flags), - e_ehsize: U16::new(endian, e_ehsize), - e_phentsize: U16::new(endian, e_phentsize), - e_phnum: U16::new(endian, e_phnum), - e_shentsize: U16::new(endian, e_shentsize), - e_shnum: U16::new(endian, e_shnum), - e_shstrndx: U16::new(endian, e_shstrndx), - }; - buffer.write(&file); - } - - Ok(()) + self.encoder.set_machine(header.e_machine); + self.encoder + .file_header(self.buffer, &self.header, &self.layout) } /// Write alignment padding bytes prior to the program headers. @@ -412,11 +287,11 @@ impl<'a, M: Mode> Writer<'a, M> { /// Returns the file offset after the padding. /// In two-phase mode, returns 0 without writing if no program headers were reserved. pub fn write_align_program_headers(&mut self) -> u64 { - if !M::need_offset(self.layout.segment_offset) { + if !M::need_offset(self.layout.e_phoff) { return 0; } - let offset = self.write_align(self.elf_align); - M::set_offset(&mut self.layout.segment_offset, offset); + let offset = self.write_align(self.encoder.address_size().into()); + M::set_offset(&mut self.layout.e_phoff, offset); offset } @@ -424,44 +299,11 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Must be called after [`Self::write_align_program_headers`]. pub fn write_program_header(&mut self, header: &ProgramHeader) { - Self::write_program_header_impl(self.buffer, self.endian, self.is_64, header); + self.encoder.program_header(self.buffer, header); self.written_segment_num += 1; M::set_count(&mut self.layout.segment_num, self.written_segment_num); } - fn write_program_header_impl( - buffer: &mut dyn WritableBuffer, - endian: Endianness, - is_64: bool, - header: &ProgramHeader, - ) { - if is_64 { - let header = elf::ProgramHeader64 { - p_type: U32::new(endian, header.p_type), - p_flags: U32::new(endian, header.p_flags), - p_offset: U64::new(endian, header.p_offset), - p_vaddr: U64::new(endian, header.p_vaddr), - p_paddr: U64::new(endian, header.p_paddr), - p_filesz: U64::new(endian, header.p_filesz), - p_memsz: U64::new(endian, header.p_memsz), - p_align: U64::new(endian, header.p_align), - }; - buffer.write(&header); - } else { - let header = elf::ProgramHeader32 { - p_type: U32::new(endian, header.p_type), - p_offset: U32::new(endian, header.p_offset as u32), - p_vaddr: U32::new(endian, header.p_vaddr as u32), - p_paddr: U32::new(endian, header.p_paddr as u32), - p_filesz: U32::new(endian, header.p_filesz as u32), - p_memsz: U32::new(endian, header.p_memsz as u32), - p_flags: U32::new(endian, header.p_flags), - p_align: U32::new(endian, header.p_align as u32), - }; - buffer.write(&header); - } - } - /// Write the null section header. /// /// This must be the first section header that is written. @@ -469,35 +311,14 @@ impl<'a, M: Mode> Writer<'a, M> { /// Returns the file offset of the header. /// In two-phase mode, returns 0 without writing if no sections were reserved. pub fn write_null_section_header(&mut self) -> u64 { - if !M::need_offset(self.layout.section_offset) { + if !M::need_offset(self.layout.e_shoff) { return 0; } - let offset = self.write_align(self.elf_align); - M::set_offset(&mut self.layout.section_offset, offset); - self.write_section_header(&SectionHeader { - name: None, - sh_type: elf::SHT_NULL, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, - sh_offset: 0, - sh_size: if self.layout.section_num >= elf::SHN_LORESERVE.into() { - self.layout.section_num.into() - } else { - 0 - }, - sh_link: if self.layout.shstrtab_index.0 >= elf::SHN_LORESERVE.into() { - self.layout.shstrtab_index.0 - } else { - 0 - }, - sh_info: if self.layout.segment_num >= elf::PN_XNUM.into() { - self.layout.segment_num - } else { - 0 - }, - sh_addralign: 0, - sh_entsize: 0, - }); + let offset = self.write_align(self.encoder.address_size().into()); + M::set_offset(&mut self.layout.e_shoff, offset); + self.encoder.null_section_header(self.buffer, &self.layout); + self.written_section_num = 1; + M::set_count(&mut self.layout.section_num, self.written_section_num); offset } @@ -505,41 +326,7 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Must be called after [`Self::write_null_section_header`]. pub fn write_section_header(&mut self, section: &SectionHeader) -> SectionIndex { - let sh_name = if let Some(name) = section.name { - self.shstrtab.get_offset(name) - } else { - 0 - }; - let endian = self.endian; - if self.is_64 { - let section = elf::SectionHeader64 { - sh_name: U32::new(endian, sh_name), - sh_type: U32::new(endian, section.sh_type), - sh_flags: U64::new(endian, section.sh_flags), - sh_addr: U64::new(endian, section.sh_addr), - sh_offset: U64::new(endian, section.sh_offset), - sh_size: U64::new(endian, section.sh_size), - sh_link: U32::new(endian, section.sh_link), - sh_info: U32::new(endian, section.sh_info), - sh_addralign: U64::new(endian, section.sh_addralign), - sh_entsize: U64::new(endian, section.sh_entsize), - }; - self.buffer.write(§ion); - } else { - let section = elf::SectionHeader32 { - sh_name: U32::new(endian, sh_name), - sh_type: U32::new(endian, section.sh_type), - sh_flags: U32::new_u64_truncate(endian, section.sh_flags), - sh_addr: U32::new(endian, section.sh_addr as u32), - sh_offset: U32::new(endian, section.sh_offset as u32), - sh_size: U32::new(endian, section.sh_size as u32), - sh_link: U32::new(endian, section.sh_link), - sh_info: U32::new(endian, section.sh_info), - sh_addralign: U32::new(endian, section.sh_addralign as u32), - sh_entsize: U32::new(endian, section.sh_entsize as u32), - }; - self.buffer.write(§ion); - } + self.encoder.section_header(self.buffer, section); let index = SectionIndex(self.written_section_num); self.written_section_num += 1; M::set_count(&mut self.layout.section_num, self.written_section_num); @@ -557,6 +344,15 @@ impl<'a, M: Mode> Writer<'a, M> { self.shstrtab.add(name) } + /// Return the offset of a section name in the section header string table. + /// + /// Returns 0 if `id` is `None`. Pairs with [`Self::add_section_name`]. + /// + /// In two-phase mode, must be called after [`Self::reserve_shstrtab`]. + pub fn section_name_offset(&self, id: Option) -> u32 { + id.map_or(0, |id| self.shstrtab.get_offset(id)) + } + /// Write the section header for the section header string table. /// /// The header is only written if the section index was reserved in two-phase @@ -567,16 +363,10 @@ impl<'a, M: Mode> Writer<'a, M> { } debug_assert_ne!(self.shstrtab_offset, 0); let index = self.write_section_header(&SectionHeader { - name: self.shstrtab_str_id, - sh_type: elf::SHT_STRTAB, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(self.shstrtab_str_id), sh_offset: self.shstrtab_offset, sh_size: self.shstrtab_size.into(), - sh_link: 0, - sh_info: 0, - sh_addralign: 1, - sh_entsize: 0, + ..self.encoder.strtab_section_header() }); M::set_section_index(&mut self.layout.shstrtab_index, index); } @@ -592,6 +382,15 @@ impl<'a, M: Mode> Writer<'a, M> { self.strtab.add(name) } + /// Return the offset of a string in the string table. + /// + /// Returns 0 if `id` is `None`. Pairs with [`Self::add_string`]. + /// + /// In two-phase mode, must be called after [`Self::reserve_strtab`]. + pub fn string_offset(&self, id: Option) -> u32 { + id.map_or(0, |id| self.strtab.get_offset(id)) + } + /// Write the section header for the string table. /// /// Only writes the header if the section index was reserved in two-phase mode, @@ -601,18 +400,12 @@ impl<'a, M: Mode> Writer<'a, M> { return; } let index = self.write_section_header(&SectionHeader { - name: self.strtab_str_id, - sh_type: elf::SHT_STRTAB, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(self.strtab_str_id), sh_offset: self.strtab_offset, sh_size: self.strtab_size.into(), - sh_link: 0, - sh_info: 0, - sh_addralign: 1, - sh_entsize: 0, + ..self.encoder.strtab_section_header() }); - M::set_section_index(&mut self.strtab_index, index); + M::set_section_index(&mut self.strtab_index.0, index); } /// Write the null symbol. @@ -625,18 +418,13 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.symtab_offset) { return 0; } - let offset = self.write_align(self.elf_align); + let offset = self.write_align(self.encoder.address_size().into()); M::set_offset(&mut self.symtab_offset, offset); M::set_section_name(&mut self.symtab_str_id, &mut self.shstrtab, b".symtab"); - if self.is_64 { - self.buffer.write(&elf::Sym64::::default()); - } else { - self.buffer.write(&elf::Sym32::::default()); - } + self.encoder.null_symbol(self.buffer); if M::need_offset(self.symtab_shndx_offset) { - self.symtab_shndx_data - .write_pod(&U32::new(self.endian, 0u32)); + self.encoder.u32(&mut self.symtab_shndx_data, 0u32); } self.written_symtab_num = 1; @@ -650,49 +438,15 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Must be called after [`Self::write_null_symbol`]. pub fn write_symbol(&mut self, sym: &Sym) -> SymbolIndex { - let st_name = if let Some(name) = sym.name { - self.strtab.get_offset(name) - } else { - 0 - }; - let st_shndx = if let Some(section) = sym.section { - elf::SymbolSection::new(section.0) - } else { - sym.st_shndx - }; - - let endian = self.endian; - if self.is_64 { - let sym = elf::Sym64 { - st_name: U32::new(endian, st_name), - st_info: sym.st_info, - st_other: sym.st_other, - st_shndx: U16::new(endian, st_shndx), - st_value: U64::new(endian, sym.st_value), - st_size: U64::new(endian, sym.st_size), - }; - self.buffer.write(&sym); - } else { - let sym = elf::Sym32 { - st_name: U32::new(endian, st_name), - st_info: sym.st_info, - st_other: sym.st_other, - st_shndx: U16::new(endian, st_shndx), - st_value: U32::new(endian, sym.st_value as u32), - st_size: U32::new(endian, sym.st_size as u32), - }; - self.buffer.write(&sym); - } - + let section = self.encoder.symbol(self.buffer, sym); if M::need_offset(self.symtab_shndx_offset) { - let section_index = if st_shndx == elf::SHN_XINDEX { + let section_index = if let Some(section) = section { self.need_symtab_shndx = true; - sym.section.map_or(0, |s| s.0) + section } else { 0 }; - self.symtab_shndx_data - .write_pod(&U32::new(self.endian, section_index)); + self.encoder.u32(&mut self.symtab_shndx_data, section_index); } let index = SymbolIndex(self.written_symtab_num); @@ -710,18 +464,14 @@ impl<'a, M: Mode> Writer<'a, M> { return; } let index = self.write_section_header(&SectionHeader { - name: self.symtab_str_id, - sh_type: elf::SHT_SYMTAB, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(self.symtab_str_id), sh_offset: self.symtab_offset, - sh_size: self.symtab_num as u64 * self.class().sym_size() as u64, - sh_link: self.strtab_index.0, - sh_info: num_local, - sh_addralign: self.elf_align as u64, - sh_entsize: self.class().sym_size() as u64, + sh_size: self.symtab_num as u64 * self.encoder.sym_size() as u64, + ..self + .encoder + .symtab_section_header(self.strtab_index.0, num_local) }); - M::set_section_index(&mut self.symtab_index, index); + M::set_section_index(&mut self.symtab_index.0, index); } /// Write the extended section indices for the symbol table. @@ -733,7 +483,7 @@ impl<'a, M: Mode> Writer<'a, M> { self.symtab_shndx_data = Vec::new(); return 0; } - let offset = self.write_align(ALIGN_SYMTAB_SHNDX); + let offset = self.write_align(ALIGN_SYMTAB_SHNDX.into()); M::set_offset(&mut self.symtab_shndx_offset, offset); M::set_section_name( &mut self.symtab_shndx_str_id, @@ -759,16 +509,12 @@ impl<'a, M: Mode> Writer<'a, M> { (self.symtab_num * 4) as u64 }; self.write_section_header(&SectionHeader { - name: self.symtab_shndx_str_id, - sh_type: elf::SHT_SYMTAB_SHNDX, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(self.symtab_shndx_str_id), sh_offset: self.symtab_shndx_offset, sh_size, - sh_link: self.symtab_index.0, - sh_info: 0, - sh_addralign: ALIGN_SYMTAB_SHNDX as u64, - sh_entsize: 4, + ..self + .encoder + .symtab_shndx_section_header(self.symtab_index.0) }); } @@ -790,6 +536,15 @@ impl<'a, M: Mode> Writer<'a, M> { self.dynstr.get_id(name) } + /// Return the offset of a string in the dynamic string table. + /// + /// Returns 0 if `id` is `None`. Pairs with [`Self::add_dynamic_string`]. + /// + /// In two-phase mode, must be called after [`Self::reserve_dynstr`]. + pub fn dynamic_string_offset(&self, id: Option) -> u32 { + id.map_or(0, |id| self.dynstr.get_offset(id)) + } + /// Write the section header for the dynamic string table. /// /// Only writes the header if the section index was reserved in two-phase mode, @@ -799,18 +554,13 @@ impl<'a, M: Mode> Writer<'a, M> { return; } let index = self.write_section_header(&SectionHeader { - name: self.dynstr_str_id, - sh_type: elf::SHT_STRTAB, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.dynstr_str_id), sh_addr, sh_offset: self.dynstr_offset, sh_size: self.dynstr_size.into(), - sh_link: 0, - sh_info: 0, - sh_addralign: 1, - sh_entsize: 0, + ..self.encoder.dynstr_section_header() }); - M::set_section_index(&mut self.dynstr_index, index); + M::set_section_index(&mut self.dynstr_index.0, index); } /// Write the null dynamic symbol. @@ -823,14 +573,11 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.dynsym_offset) { return 0; } - let offset = self.write_align(self.elf_align); + let offset = self.write_align(self.encoder.address_size().into()); M::set_offset(&mut self.dynsym_offset, offset); M::set_section_name(&mut self.dynsym_str_id, &mut self.shstrtab, b".dynsym"); - if self.is_64 { - self.buffer.write(&elf::Sym64::::default()); - } else { - self.buffer.write(&elf::Sym32::::default()); - } + + self.encoder.null_symbol(self.buffer); self.written_dynsym_num = 1; M::set_count(&mut self.dynsym_num, self.written_dynsym_num); @@ -843,42 +590,9 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Must be called after [`Self::write_null_dynamic_symbol`]. pub fn write_dynamic_symbol(&mut self, sym: &Sym) -> SymbolIndex { - let st_name = if let Some(name) = sym.name { - self.dynstr.get_offset(name) - } else { - 0 - }; - - let st_shndx = if let Some(section) = sym.section { - // TODO: we don't write out .dynsym_shndx yet. - // This is unlikely to be needed though. - elf::SymbolSection::new(section.0) - } else { - sym.st_shndx - }; - - let endian = self.endian; - if self.is_64 { - let sym = elf::Sym64 { - st_name: U32::new(endian, st_name), - st_info: sym.st_info, - st_other: sym.st_other, - st_shndx: U16::new(endian, st_shndx), - st_value: U64::new(endian, sym.st_value), - st_size: U64::new(endian, sym.st_size), - }; - self.buffer.write(&sym); - } else { - let sym = elf::Sym32 { - st_name: U32::new(endian, st_name), - st_info: sym.st_info, - st_other: sym.st_other, - st_shndx: U16::new(endian, st_shndx), - st_value: U32::new(endian, sym.st_value as u32), - st_size: U32::new(endian, sym.st_size as u32), - }; - self.buffer.write(&sym); - } + // TODO: we don't write out .dynsym_shndx yet. + // This is unlikely to be needed though. + let _ = self.encoder.symbol(self.buffer, sym); let index = SymbolIndex(self.written_dynsym_num); self.written_dynsym_num += 1; @@ -895,18 +609,15 @@ impl<'a, M: Mode> Writer<'a, M> { return; } let index = self.write_section_header(&SectionHeader { - name: self.dynsym_str_id, - sh_type: elf::SHT_DYNSYM, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.dynsym_str_id), sh_addr, sh_offset: self.dynsym_offset, - sh_size: self.dynsym_num as u64 * self.class().sym_size() as u64, - sh_link: self.dynstr_index.0, - sh_info: num_local, - sh_addralign: self.elf_align as u64, - sh_entsize: self.class().sym_size() as u64, + sh_size: self.dynsym_num as u64 * self.encoder.sym_size() as u64, + ..self + .encoder + .dynsym_section_header(self.dynstr_index.0, num_local) }); - M::set_section_index(&mut self.dynsym_index, index); + M::set_section_index(&mut self.dynsym_index.0, index); } /// Write alignment padding bytes prior to the `.dynamic` section. @@ -917,7 +628,7 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.dynamic_offset) { return 0; } - let offset = self.write_align(self.elf_align); + let offset = self.write_align(self.encoder.address_size().into()); M::set_offset(&mut self.dynamic_offset, offset); M::set_section_name(&mut self.dynamic_str_id, &mut self.shstrtab, b".dynamic"); offset @@ -934,25 +645,7 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Must be called after [`Self::write_align_dynamic`]. pub fn write_dynamic(&mut self, d_tag: elf::DynamicTag, d_val: u64) -> Result<()> { - let endian = self.endian; - if self.is_64 { - let d = elf::Dyn64 { - d_tag: I64::new(endian, d_tag), - d_val: U64::new(endian, d_val), - }; - self.buffer.write(&d); - } else { - let d_tag = I32::new_i64(endian, d_tag) - .map_err(|_| Error(format!("d_tag overflow: 0x{:x}", d_tag)))?; - let d_val = d_val - .try_into() - .map_err(|_| Error(format!("d_val overflow: 0x{:x}", d_val)))?; - let d = elf::Dyn32 { - d_tag, - d_val: U32::new(endian, d_val), - }; - self.buffer.write(&d); - } + self.encoder.dynamic(self.buffer, d_tag, d_val)?; self.written_dynamic_num += 1; M::set_count(&mut self.dynamic_num, self.written_dynamic_num); Ok(()) @@ -967,50 +660,31 @@ impl<'a, M: Mode> Writer<'a, M> { return; } self.write_section_header(&SectionHeader { - name: self.dynamic_str_id, - sh_type: elf::SHT_DYNAMIC, - sh_flags: elf::SHF_WRITE | elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.dynamic_str_id), sh_addr, sh_offset: self.dynamic_offset, - sh_size: (self.dynamic_num * self.class().dyn_size()) as u64, - sh_link: self.dynstr_index.0, - sh_info: 0, - sh_addralign: self.elf_align as u64, - sh_entsize: self.class().dyn_size() as u64, + sh_size: (self.dynamic_num * self.encoder.dyn_size()) as u64, + ..self.encoder.dynamic_section_header(self.dynstr_index.0) }); } /// Write a SysV hash section. /// - /// `chain_count` is the number of symbols in the hash. - /// The argument to `hash` will be in the range `0..chain_count`. + /// `symbol_count` is the number of symbols in the hash. + /// The argument to `hash` will be in the range `0..symbol_count`. /// /// In two-phase mode, [`Self::reserve_hash`] must be called before this. /// /// Returns the file offset of the hash table data. - pub fn write_hash(&mut self, bucket_count: u32, chain_count: u32, hash: F) -> u64 + pub fn write_hash(&mut self, bucket_count: u32, symbol_count: u32, hash: F) -> u64 where F: Fn(u32) -> Option, { - let mut buckets = vec![U32::new(self.endian, 0); bucket_count as usize]; - let mut chains = vec![U32::new(self.endian, 0); chain_count as usize]; - for i in 0..chain_count { - if let Some(hash) = hash(i) { - let bucket = hash % bucket_count; - chains[i as usize] = buckets[bucket as usize]; - buckets[bucket as usize] = U32::new(self.endian, i); - } - } - - let offset = self.write_align(ALIGN_HASH); + let offset = self.write_align(ALIGN_HASH.into()); M::set_offset(&mut self.hash_offset, offset); M::set_section_name(&mut self.hash_str_id, &mut self.shstrtab, b".hash"); - self.buffer.write(&elf::HashHeader { - bucket_count: U32::new(self.endian, bucket_count), - chain_count: U32::new(self.endian, chain_count), - }); - self.buffer.write_slice(&buckets); - self.buffer.write_slice(&chains); + self.encoder + .hash_table(self.buffer, bucket_count, symbol_count, hash); let size = self.offset() - self.hash_offset; M::set_size(&mut self.hash_size, size); offset @@ -1025,16 +699,11 @@ impl<'a, M: Mode> Writer<'a, M> { return; } self.write_section_header(&SectionHeader { - name: self.hash_str_id, - sh_type: elf::SHT_HASH, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.hash_str_id), sh_addr, sh_offset: self.hash_offset, sh_size: self.hash_size, - sh_link: self.dynsym_index.0, - sh_info: 0, - sh_addralign: ALIGN_HASH as u64, - sh_entsize: 4, + ..self.encoder.hash_section_header(self.dynsym_index.0) }); } @@ -1060,69 +729,21 @@ impl<'a, M: Mode> Writer<'a, M> { where F: Fn(u32) -> u32, { - let offset = self.write_align(self.elf_align); + let offset = self.write_align(self.encoder.address_size().into()); M::set_offset(&mut self.gnu_hash_offset, offset); M::set_section_name(&mut self.gnu_hash_str_id, &mut self.shstrtab, b".gnu.hash"); - self.buffer.write(&elf::GnuHashHeader { - bucket_count: U32::new(self.endian, bucket_count), - symbol_base: U32::new(self.endian, symbol_base), - bloom_count: U32::new(self.endian, bloom_count), - bloom_shift: U32::new(self.endian, bloom_shift), - }); - // Calculate and write bloom filter. - if self.is_64 { - let mut bloom_filters = vec![0u64; bloom_count as usize]; - for i in 0..symbol_count { - let h = hash(i); - bloom_filters[((h / 64) & (bloom_count - 1)) as usize] |= - 1 << (h % 64) | 1 << ((h >> bloom_shift) % 64); - } - for bloom_filter in bloom_filters { - self.buffer.write(&U64::new(self.endian, bloom_filter)); - } - } else { - let mut bloom_filters = vec![0u32; bloom_count as usize]; - for i in 0..symbol_count { - let h = hash(i); - bloom_filters[((h / 32) & (bloom_count - 1)) as usize] |= - 1 << (h % 32) | 1 << ((h >> bloom_shift) % 32); - } - for bloom_filter in bloom_filters { - self.buffer.write(&U32::new(self.endian, bloom_filter)); - } - } - - // Write buckets. - // - // This requires that symbols are already sorted by bucket. - let mut bucket = 0; - for i in 0..symbol_count { - let symbol_bucket = hash(i) % bucket_count; - while bucket < symbol_bucket { - self.buffer.write(&U32::new(self.endian, 0u32)); - bucket += 1; - } - if bucket == symbol_bucket { - self.buffer.write(&U32::new(self.endian, symbol_base + i)); - bucket += 1; - } - } - while bucket < bucket_count { - self.buffer.write(&U32::new(self.endian, 0u32)); - bucket += 1; - } - - // Write hash values. - for i in 0..symbol_count { - let mut h = hash(i); - if i == symbol_count - 1 || h % bucket_count != hash(i + 1) % bucket_count { - h |= 1; - } else { - h &= !1; - } - self.buffer.write(&U32::new(self.endian, h)); - } + self.encoder.gnu_hash_table( + self.buffer, + &GnuHashTable { + bloom_shift, + bloom_count, + bucket_count, + symbol_base, + symbol_count, + }, + hash, + ); let size = self.offset() - self.gnu_hash_offset; M::set_size(&mut self.gnu_hash_size, size); @@ -1138,16 +759,11 @@ impl<'a, M: Mode> Writer<'a, M> { return; } self.write_section_header(&SectionHeader { - name: self.gnu_hash_str_id, - sh_type: elf::SHT_GNU_HASH, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.gnu_hash_str_id), sh_addr, sh_offset: self.gnu_hash_offset, sh_size: self.gnu_hash_size, - sh_link: self.dynsym_index.0, - sh_info: 0, - sh_addralign: self.elf_align as u64, - sh_entsize: if self.is_64 { 0 } else { 4 }, + ..self.encoder.gnu_hash_section_header(self.dynsym_index.0) }); } @@ -1161,7 +777,7 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.gnu_versym_offset) { return 0; } - let offset = self.write_align(ALIGN_GNU_VERSYM); + let offset = self.write_align(ALIGN_GNU_VERSYM.into()); M::set_offset(&mut self.gnu_versym_offset, offset); M::set_section_name( &mut self.gnu_versym_str_id, @@ -1176,7 +792,7 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Must be called after [`Self::write_null_gnu_versym`]. pub fn write_gnu_versym(&mut self, versym: elf::VersymIndex) { - self.buffer.write(&U16::new(self.endian, versym)); + self.encoder.gnu_versym(self.buffer, versym); } /// Write the section header for the `.gnu.version` section. @@ -1188,16 +804,11 @@ impl<'a, M: Mode> Writer<'a, M> { return; } self.write_section_header(&SectionHeader { - name: self.gnu_versym_str_id, - sh_type: elf::SHT_GNU_VERSYM, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.gnu_versym_str_id), sh_addr, sh_offset: self.gnu_versym_offset, - sh_size: self.class().gnu_versym_size(self.dynsym_num as usize) as u64, - sh_link: self.dynsym_index.0, - sh_info: 0, - sh_addralign: ALIGN_GNU_VERSYM as u64, - sh_entsize: 2, + sh_size: self.encoder.gnu_versym_size(self.dynsym_num as usize) as u64, + ..self.encoder.gnu_versym_section_header(self.dynsym_index.0) }); } @@ -1209,7 +820,7 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.gnu_verdef_offset) { return 0; } - let offset = self.write_align(ALIGN_GNU_VERDEF); + let offset = self.write_align(ALIGN_GNU_VERDEF.into()); M::set_offset(&mut self.gnu_verdef_offset, offset); M::set_section_name( &mut self.gnu_verdef_str_id, @@ -1227,29 +838,14 @@ impl<'a, M: Mode> Writer<'a, M> { pub fn write_gnu_verdef(&mut self, verdef: &Verdef) { debug_assert_ne!(self.gnu_verdef_remaining, 0); self.gnu_verdef_remaining -= 1; - let vd_next = if self.gnu_verdef_remaining == 0 { - 0 - } else { - mem::size_of::>() as u32 - + verdef.aux_count as u32 * mem::size_of::>() as u32 - }; debug_assert_ne!(verdef.aux_count, 0); self.gnu_verdaux_remaining = verdef.aux_count; self.written_verdaux_count += verdef.aux_count as usize; M::set_count(&mut self.gnu_verdaux_count, self.written_verdaux_count); - let vd_aux = mem::size_of::>() as u32; - self.buffer.write(&elf::Verdef { - vd_version: U16::new(self.endian, verdef.version), - vd_flags: U16::new(self.endian, verdef.flags), - vd_ndx: U16::new(self.endian, verdef.index), - vd_cnt: U16::new(self.endian, verdef.aux_count), - vd_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(verdef.name))), - vd_aux: U32::new(self.endian, vd_aux), - vd_next: U32::new(self.endian, vd_next), - }); - self.write_gnu_verdaux(verdef.name); + self.encoder + .gnu_verdef(self.buffer, self.gnu_verdef_remaining != 0, verdef); } /// Write a version definition entry that shares the names of the next definition. @@ -1264,21 +860,11 @@ impl<'a, M: Mode> Writer<'a, M> { debug_assert_ne!(self.gnu_verdef_remaining, 0); self.gnu_verdef_remaining -= 1; debug_assert_ne!(self.gnu_verdef_remaining, 0); - let vd_next = mem::size_of::>() as u32; debug_assert_ne!(verdef.aux_count, 0); self.gnu_verdaux_remaining = 0; - let vd_aux = 2 * mem::size_of::>() as u32; - self.buffer.write(&elf::Verdef { - vd_version: U16::new(self.endian, verdef.version), - vd_flags: U16::new(self.endian, verdef.flags), - vd_ndx: U16::new(self.endian, verdef.index), - vd_cnt: U16::new(self.endian, verdef.aux_count), - vd_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(verdef.name))), - vd_aux: U32::new(self.endian, vd_aux), - vd_next: U32::new(self.endian, vd_next), - }); + self.encoder.gnu_verdef_shared(self.buffer, verdef); } /// Write a version definition auxiliary entry. @@ -1287,15 +873,11 @@ impl<'a, M: Mode> Writer<'a, M> { pub fn write_gnu_verdaux(&mut self, name: StringId) { debug_assert_ne!(self.gnu_verdaux_remaining, 0); self.gnu_verdaux_remaining -= 1; - let vda_next = if self.gnu_verdaux_remaining == 0 { - 0 - } else { - mem::size_of::>() as u32 - }; - self.buffer.write(&elf::Verdaux { - vda_name: U32::new(self.endian, self.dynstr.get_offset(name)), - vda_next: U32::new(self.endian, vda_next), - }); + self.encoder.gnu_verdaux( + self.buffer, + self.gnu_verdaux_remaining != 0, + self.dynstr.get_offset(name), + ); } /// Write the section header for the `.gnu.version_d` section. @@ -1307,20 +889,17 @@ impl<'a, M: Mode> Writer<'a, M> { return; } let sh_size = self - .class() + .encoder .gnu_verdef_size(self.gnu_verdef_count as usize, self.gnu_verdaux_count) as u64; self.write_section_header(&SectionHeader { - name: self.gnu_verdef_str_id, - sh_type: elf::SHT_GNU_VERDEF, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.gnu_verdef_str_id), sh_addr, sh_offset: self.gnu_verdef_offset, sh_size, - sh_link: self.dynstr_index.0, - sh_info: self.gnu_verdef_count.into(), - sh_addralign: ALIGN_GNU_VERDEF as u64, - sh_entsize: 0, + ..self + .encoder + .gnu_verdef_section_header(self.dynstr_index.0, self.gnu_verdef_count.into()) }); } @@ -1332,7 +911,7 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.gnu_verneed_offset) { return 0; } - let offset = self.write_align(ALIGN_GNU_VERNEED); + let offset = self.write_align(ALIGN_GNU_VERNEED.into()); M::set_offset(&mut self.gnu_verneed_offset, offset); M::set_section_name( &mut self.gnu_verneed_str_id, @@ -1342,7 +921,7 @@ impl<'a, M: Mode> Writer<'a, M> { offset } - /// Write a version need entry. + /// Write a version needed entry. /// /// Must be called after [`Self::write_align_gnu_verneed`]. The number of entries /// must have been set via [`Self::reserve_gnu_verneed`] in two-phase mode, or @@ -1350,49 +929,25 @@ impl<'a, M: Mode> Writer<'a, M> { pub fn write_gnu_verneed(&mut self, verneed: &Verneed) { debug_assert_ne!(self.gnu_verneed_remaining, 0); self.gnu_verneed_remaining -= 1; - let vn_next = if self.gnu_verneed_remaining == 0 { - 0 - } else { - mem::size_of::>() as u32 - + verneed.aux_count as u32 * mem::size_of::>() as u32 - }; - let vn_aux = if verneed.aux_count == 0 { - 0 - } else { + if verneed.aux_count != 0 { self.gnu_vernaux_remaining = verneed.aux_count; self.written_vernaux_count += verneed.aux_count as usize; M::set_count(&mut self.gnu_vernaux_count, self.written_vernaux_count); - mem::size_of::>() as u32 }; - self.buffer.write(&elf::Verneed { - vn_version: U16::new(self.endian, verneed.version), - vn_cnt: U16::new(self.endian, verneed.aux_count), - vn_file: U32::new(self.endian, self.dynstr.get_offset(verneed.file)), - vn_aux: U32::new(self.endian, vn_aux), - vn_next: U32::new(self.endian, vn_next), - }); + self.encoder + .gnu_verneed(self.buffer, self.gnu_verneed_remaining != 0, verneed); } - /// Write a version need auxiliary entry. + /// Write a version needed auxiliary entry. /// - /// Must be called inside a version need started by [`Self::write_gnu_verneed`]. + /// Must be called inside a version needed entry started by [`Self::write_gnu_verneed`]. pub fn write_gnu_vernaux(&mut self, vernaux: &Vernaux) { debug_assert_ne!(self.gnu_vernaux_remaining, 0); self.gnu_vernaux_remaining -= 1; - let vna_next = if self.gnu_vernaux_remaining == 0 { - 0 - } else { - mem::size_of::>() as u32 - }; - self.buffer.write(&elf::Vernaux { - vna_hash: U32::new(self.endian, elf::hash(self.dynstr.get_string(vernaux.name))), - vna_flags: U16::new(self.endian, vernaux.flags), - vna_other: U16::new(self.endian, vernaux.index), - vna_name: U32::new(self.endian, self.dynstr.get_offset(vernaux.name)), - vna_next: U32::new(self.endian, vna_next), - }); + self.encoder + .gnu_vernaux(self.buffer, self.gnu_vernaux_remaining != 0, vernaux); } /// Write the section header for the `.gnu.version_r` section. @@ -1404,20 +959,17 @@ impl<'a, M: Mode> Writer<'a, M> { return; } let sh_size = self - .class() + .encoder .gnu_verneed_size(self.gnu_verneed_count as usize, self.gnu_vernaux_count) as u64; self.write_section_header(&SectionHeader { - name: self.gnu_verneed_str_id, - sh_type: elf::SHT_GNU_VERNEED, - sh_flags: elf::SHF_ALLOC, + sh_name: self.section_name_offset(self.gnu_verneed_str_id), sh_addr, sh_offset: self.gnu_verneed_offset, sh_size, - sh_link: self.dynstr_index.0, - sh_info: self.gnu_verneed_count.into(), - sh_addralign: ALIGN_GNU_VERNEED as u64, - sh_entsize: 0, + ..self + .encoder + .gnu_verneed_section_header(self.dynstr_index.0, self.gnu_verneed_count.into()) }); } @@ -1430,16 +982,11 @@ impl<'a, M: Mode> Writer<'a, M> { return; } self.write_section_header(&SectionHeader { - name: self.gnu_attributes_str_id, - sh_type: elf::SHT_GNU_ATTRIBUTES, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(self.gnu_attributes_str_id), sh_offset: self.gnu_attributes_offset, sh_size: self.gnu_attributes_size, sh_link: self.dynstr_index.0, - sh_info: 0, // TODO - sh_addralign: self.elf_align as u64, - sh_entsize: 0, + ..self.encoder.gnu_attributes_section_header() }); } @@ -1451,7 +998,7 @@ impl<'a, M: Mode> Writer<'a, M> { if !M::need_offset(self.gnu_attributes_offset) { return 0; } - let offset = self.write_align(self.elf_align); + let offset = self.write_align(self.encoder.address_size().into()); M::set_offset(&mut self.gnu_attributes_offset, offset); M::set_section_name( &mut self.gnu_attributes_str_id, @@ -1468,7 +1015,7 @@ impl<'a, M: Mode> Writer<'a, M> { /// /// Returns the file offset after the padding. pub fn write_align_relocation(&mut self) -> u64 { - self.write_align(self.elf_align) + self.write_align(self.encoder.address_size().into()) } /// Write a relocation. @@ -1477,38 +1024,7 @@ impl<'a, M: Mode> Writer<'a, M> { /// of a section. In two-phase mode, the file range must have been reserved with /// [`Self::reserve_relocations`]. pub fn write_relocation(&mut self, is_rela: bool, rel: &Rel) { - let endian = self.endian; - if self.is_64 { - if is_rela { - let rel = elf::Rela64 { - r_offset: U64::new(endian, rel.r_offset), - r_info: elf::Rela64::r_info(endian, self.is_mips64el, rel.r_sym, rel.r_type), - r_addend: I64::new(endian, rel.r_addend), - }; - self.buffer.write(&rel); - } else { - let rel = elf::Rel64 { - r_offset: U64::new(endian, rel.r_offset), - r_info: elf::Rel64::r_info(endian, rel.r_sym, rel.r_type), - }; - self.buffer.write(&rel); - } - } else { - if is_rela { - let rel = elf::Rela32 { - r_offset: U32::new(endian, rel.r_offset as u32), - r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), - r_addend: I32::new(endian, rel.r_addend as i32), - }; - self.buffer.write(&rel); - } else { - let rel = elf::Rel32 { - r_offset: U32::new(endian, rel.r_offset as u32), - r_info: elf::Rel32::r_info(endian, rel.r_sym, rel.r_type as u8), - }; - self.buffer.write(&rel); - } - } + self.encoder.relocation(self.buffer, is_rela, rel); } /// Write the section header for a relocation section. @@ -1530,16 +1046,12 @@ impl<'a, M: Mode> Writer<'a, M> { is_rela: bool, ) { self.write_section_header(&SectionHeader { - name: Some(name), - sh_type: if is_rela { elf::SHT_RELA } else { elf::SHT_REL }, - sh_flags: elf::SHF_INFO_LINK, - sh_addr: 0, + sh_name: self.section_name_offset(Some(name)), sh_offset: offset, - sh_size: (count * self.class().rel_size(is_rela)) as u64, + sh_size: (count * self.encoder.rel_size(is_rela)) as u64, sh_link: symtab.0, sh_info: section.0, - sh_addralign: self.elf_align as u64, - sh_entsize: self.class().rel_size(is_rela) as u64, + ..self.encoder.relocation_section_header(is_rela) }); } @@ -1554,28 +1066,22 @@ impl<'a, M: Mode> Writer<'a, M> { size: usize, ) { self.write_section_header(&SectionHeader { - name: Some(name), - sh_type: elf::SHT_RELA, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(Some(name)), sh_offset: offset as u64, sh_size: size as u64, - sh_link: 0, - sh_info: 0, - sh_addralign: self.elf_align as u64, - sh_entsize: self.class().relr_size() as u64, + ..self.encoder.relative_relocation_section_header() }); } /// Write `GRP_COMDAT` at the start of the COMDAT section. pub fn write_comdat_header(&mut self) { util::write_align(self.buffer, 4); - self.buffer.write(&U32::new(self.endian, elf::GRP_COMDAT)); + self.encoder.u32(self.buffer, elf::GRP_COMDAT); } /// Write an entry in a COMDAT section. pub fn write_comdat_entry(&mut self, entry: SectionIndex) { - self.buffer.write(&U32::new(self.endian, entry.0)); + self.encoder.u32(self.buffer, entry.0); } /// Write the section header for a COMDAT section. @@ -1588,22 +1094,18 @@ impl<'a, M: Mode> Writer<'a, M> { count: usize, ) { self.write_section_header(&SectionHeader { - name: Some(name), - sh_type: elf::SHT_GROUP, - sh_flags: elf::SectionFlags(0), - sh_addr: 0, + sh_name: self.section_name_offset(Some(name)), sh_offset: offset, sh_size: ((count + 1) * 4) as u64, sh_link: symtab.0, sh_info: symbol.0, - sh_addralign: 4, - sh_entsize: 4, + ..self.encoder.comdat_section_header() }); } /// Return a helper for writing an attributes section. pub fn attributes_writer(&self) -> AttributesWriter { - AttributesWriter::new(self.endian) + AttributesWriter::new(self.encoder.endian()) } } @@ -1667,9 +1169,9 @@ impl ModeSealed for SinglePhase { *dest = size; } - fn set_section_index(dest: &mut SectionIndex, index: SectionIndex) { - debug_assert_eq!(dest.0, 0); - *dest = index; + fn set_section_index(dest: &mut u32, index: SectionIndex) { + debug_assert_eq!(*dest, 0); + *dest = index.0; } fn set_section_name( @@ -1710,7 +1212,7 @@ impl<'a> Writer<'a, SinglePhase> { "single phase write doesn't support e_shnum overflow", ))); } - Self::write_file_header_impl(buffer, self.endian, self.is_64, &self.header, &self.layout) + self.encoder.file_header(buffer, &self.header, &self.layout) } /// Write a placeholder for the program header table immediately after the file header. @@ -1726,14 +1228,14 @@ impl<'a> Writer<'a, SinglePhase> { if count == 0 { return (0, 0); } - // file_header_size is always a multiple of elf_align, so no alignment + // file_header_size is always a multiple of address size, so no alignment // padding is required. let offset = self.offset(); - debug_assert_eq!(offset, self.class().file_header_size() as u64); - SinglePhase::set_offset(&mut self.layout.segment_offset, offset); + debug_assert_eq!(offset, self.encoder.file_header_size() as u64); + SinglePhase::set_offset(&mut self.layout.e_phoff, offset); SinglePhase::set_count(&mut self.layout.segment_num, count); self.written_segment_num = count; - let size = count as usize * self.class().program_header_size(); + let size = count as usize * self.encoder.program_header_size(); self.buffer.resize(self.buffer.len() + size); (offset, size as u64) } @@ -1753,9 +1255,10 @@ impl<'a> Writer<'a, SinglePhase> { program_headers: &[ProgramHeader], ) -> Result<()> { debug_assert_eq!(program_headers.len() as u32, self.layout.segment_num); - Self::write_file_header_impl(buffer, self.endian, self.is_64, &self.header, &self.layout)?; + self.encoder + .file_header(buffer, &self.header, &self.layout)?; for header in program_headers { - Self::write_program_header_impl(buffer, self.endian, self.is_64, header); + self.encoder.program_header(buffer, header); } Ok(()) } @@ -1769,7 +1272,7 @@ impl<'a> Writer<'a, SinglePhase> { buffer: &mut dyn WritableBuffer, header: &ProgramHeader, ) { - Self::write_program_header_impl(buffer, self.endian, self.is_64, header); + self.encoder.program_header(buffer, header); } /// Write the section header string table. @@ -1901,8 +1404,8 @@ impl ModeSealed for TwoPhase { debug_assert_eq!(*dest, size); } - fn set_section_index(dest: &mut SectionIndex, index: SectionIndex) { - debug_assert_eq!(*dest, index); + fn set_section_index(dest: &mut u32, index: SectionIndex) { + debug_assert_eq!(*dest, index.0); } fn set_section_name( @@ -1960,7 +1463,7 @@ impl<'a> Writer<'a, TwoPhase> { /// This must be at the start of the file. pub fn reserve_file_header(&mut self) { debug_assert_eq!(self.mode.len, 0); - self.reserve(self.class().file_header_size(), 1); + self.reserve(self.encoder.file_header_size(), 1); } /// Reserve the range for the program headers. @@ -1970,14 +1473,14 @@ impl<'a> Writer<'a, TwoPhase> { /// /// Does nothing if `num` is zero. pub fn reserve_program_headers(&mut self, num: u32) { - debug_assert_eq!(self.layout.segment_offset, 0); + debug_assert_eq!(self.layout.e_phoff, 0); if num == 0 { return; } self.layout.segment_num = num; - self.layout.segment_offset = self.reserve( - num as usize * self.class().program_header_size(), - self.elf_align, + self.layout.e_phoff = self.reserve( + num as usize * self.encoder.program_header_size(), + self.encoder.address_size().into(), ); } @@ -2002,7 +1505,7 @@ impl<'a> Writer<'a, TwoPhase> { /// /// Must be called before [`Self::reserve_section_headers`]. pub fn reserve_section_index(&mut self) -> SectionIndex { - debug_assert_eq!(self.layout.section_offset, 0); + debug_assert_eq!(self.layout.e_shoff, 0); if self.layout.section_num == 0 { self.layout.section_num = 1; } @@ -2017,13 +1520,13 @@ impl<'a> Writer<'a, TwoPhase> { /// Must be called after [`Self::reserve_section_index`] /// and other functions that reserve section indices. pub fn reserve_section_headers(&mut self) { - debug_assert_eq!(self.layout.section_offset, 0); + debug_assert_eq!(self.layout.e_shoff, 0); if self.layout.section_num == 0 { return; } - self.layout.section_offset = self.reserve( - self.layout.section_num as usize * self.class().section_header_size(), - self.elf_align, + self.layout.e_shoff = self.reserve( + self.layout.section_num as usize * self.encoder.section_header_size(), + self.encoder.address_size().into(), ); } @@ -2072,10 +1575,10 @@ impl<'a> Writer<'a, TwoPhase> { /// Must be called before [`Self::reserve_shstrtab`] /// and [`Self::reserve_section_headers`]. pub fn reserve_shstrtab_section_index_with_name(&mut self, name: &'a [u8]) -> SectionIndex { - debug_assert_eq!(self.layout.shstrtab_index, SectionIndex(0)); + debug_assert_eq!(self.layout.shstrtab_index, 0); self.shstrtab_str_id = Some(self.add_section_name(name)); - self.layout.shstrtab_index = self.reserve_section_index(); - self.layout.shstrtab_index + self.layout.shstrtab_index = self.reserve_section_index().0; + SectionIndex(self.layout.shstrtab_index) } /// Return true if `.strtab` is needed. @@ -2209,8 +1712,8 @@ impl<'a> Writer<'a, TwoPhase> { return; } self.symtab_offset = self.reserve( - self.symtab_num as usize * self.class().sym_size(), - self.elf_align, + self.symtab_num as usize * self.encoder.sym_size(), + self.encoder.address_size().into(), ); } @@ -2259,7 +1762,8 @@ impl<'a> Writer<'a, TwoPhase> { if !self.symtab_shndx_needed() { return; } - self.symtab_shndx_offset = self.reserve(self.symtab_num as usize * 4, ALIGN_SYMTAB_SHNDX); + self.symtab_shndx_offset = + self.reserve(self.symtab_num as usize * 4, ALIGN_SYMTAB_SHNDX.into()); self.symtab_shndx_data.reserve(self.symtab_num as usize * 4); } @@ -2421,8 +1925,8 @@ impl<'a> Writer<'a, TwoPhase> { return 0; } self.dynsym_offset = self.reserve( - self.dynsym_num as usize * self.class().sym_size(), - self.elf_align, + self.dynsym_num as usize * self.encoder.sym_size(), + self.encoder.address_size().into(), ); self.dynsym_offset } @@ -2467,7 +1971,10 @@ impl<'a> Writer<'a, TwoPhase> { /// Returns the offset of the range. pub fn reserve_dynamics(&mut self, dynamic_num: usize) -> u64 { self.dynamic_num += dynamic_num; - self.reserve(dynamic_num * self.class().dyn_size(), self.elf_align) + self.reserve( + dynamic_num * self.encoder.dyn_size(), + self.encoder.address_size().into(), + ) } /// Reserve the section index for the dynamic table. @@ -2483,9 +1990,9 @@ impl<'a> Writer<'a, TwoPhase> { /// /// `symbol_count` is the number of symbols in the hash, /// not the total number of symbols. - pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) -> u64 { - let size = self.class().hash_size(bucket_count, chain_count); - self.hash_offset = self.reserve(size, ALIGN_HASH); + pub fn reserve_hash(&mut self, bucket_count: u32, symbol_count: u32) -> u64 { + let size = self.encoder.hash_size(bucket_count, symbol_count); + self.hash_offset = self.reserve(size, ALIGN_HASH.into()); self.hash_size = size as u64; self.hash_offset } @@ -2517,9 +2024,9 @@ impl<'a> Writer<'a, TwoPhase> { symbol_count: u32, ) -> u64 { let size = self - .class() + .encoder .gnu_hash_size(bloom_count, bucket_count, symbol_count); - self.gnu_hash_offset = self.reserve(size, self.elf_align); + self.gnu_hash_offset = self.reserve(size, self.encoder.address_size().into()); self.gnu_hash_size = size as u64; self.gnu_hash_offset } @@ -2549,7 +2056,8 @@ impl<'a> Writer<'a, TwoPhase> { if self.dynsym_num == 0 { return 0; } - self.gnu_versym_offset = self.reserve(self.dynsym_num as usize * 2, ALIGN_GNU_VERSYM); + self.gnu_versym_offset = + self.reserve(self.dynsym_num as usize * 2, ALIGN_GNU_VERSYM.into()); self.gnu_versym_offset } @@ -2578,8 +2086,8 @@ impl<'a> Writer<'a, TwoPhase> { if verdef_count == 0 { return 0; } - let size = self.class().gnu_verdef_size(verdef_count, verdaux_count); - self.gnu_verdef_offset = self.reserve(size, ALIGN_GNU_VERDEF); + let size = self.encoder.gnu_verdef_size(verdef_count, verdaux_count); + self.gnu_verdef_offset = self.reserve(size, ALIGN_GNU_VERDEF.into()); self.gnu_verdef_count = verdef_count as u16; self.gnu_verdaux_count = verdaux_count; self.gnu_verdef_remaining = self.gnu_verdef_count; @@ -2611,8 +2119,8 @@ impl<'a> Writer<'a, TwoPhase> { if verneed_count == 0 { return 0; } - let size = self.class().gnu_verneed_size(verneed_count, vernaux_count); - self.gnu_verneed_offset = self.reserve(size, ALIGN_GNU_VERNEED); + let size = self.encoder.gnu_verneed_size(verneed_count, vernaux_count); + self.gnu_verneed_offset = self.reserve(size, ALIGN_GNU_VERNEED.into()); self.gnu_verneed_count = verneed_count as u16; self.gnu_vernaux_count = vernaux_count; self.gnu_verneed_remaining = self.gnu_verneed_count; @@ -2664,7 +2172,8 @@ impl<'a> Writer<'a, TwoPhase> { return 0; } self.gnu_attributes_size = gnu_attributes_size as u64; - self.gnu_attributes_offset = self.reserve(gnu_attributes_size, self.elf_align); + self.gnu_attributes_offset = + self.reserve(gnu_attributes_size, self.encoder.address_size().into()); self.gnu_attributes_offset } @@ -2672,7 +2181,10 @@ impl<'a> Writer<'a, TwoPhase> { /// /// Returns the offset of the range. pub fn reserve_relocations(&mut self, count: usize, is_rela: bool) -> u64 { - self.reserve(count * self.class().rel_size(is_rela), self.elf_align) + self.reserve( + count * self.encoder.rel_size(is_rela), + self.encoder.address_size().into(), + ) } /// Reserve a file range for a COMDAT section. @@ -2809,225 +2321,3 @@ impl AttributesWriter { self.data } } - -/// An ELF file class. -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -pub struct Class { - /// Whether the file is 64-bit. - pub is_64: bool, -} - -impl Class { - /// Return the alignment size. - pub fn align(self) -> usize { - if self.is_64 { 8 } else { 4 } - } - - /// Return the size of the file header. - pub fn file_header_size(self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Return the size of a program header. - pub fn program_header_size(self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Return the size of a section header. - pub fn section_header_size(self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Return the size of a symbol. - pub fn sym_size(self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Return the size of a relocation entry. - pub fn rel_size(self, is_rela: bool) -> usize { - if self.is_64 { - if is_rela { - mem::size_of::>() - } else { - mem::size_of::>() - } - } else { - if is_rela { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - } - - /// Return the size of a relative relocation entry. - pub fn relr_size(self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Return the size of a dynamic entry. - pub fn dyn_size(self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Return the size of a hash table. - pub fn hash_size(self, bucket_count: u32, chain_count: u32) -> usize { - mem::size_of::>() - + bucket_count as usize * 4 - + chain_count as usize * 4 - } - - /// Return the size of a GNU hash table. - pub fn gnu_hash_size(self, bloom_count: u32, bucket_count: u32, symbol_count: u32) -> usize { - let bloom_size = if self.is_64 { 8 } else { 4 }; - mem::size_of::>() - + bloom_count as usize * bloom_size - + bucket_count as usize * 4 - + symbol_count as usize * 4 - } - - /// Return the size of a GNU symbol version section. - pub fn gnu_versym_size(self, symbol_count: usize) -> usize { - symbol_count * 2 - } - - /// Return the size of a GNU version definition section. - pub fn gnu_verdef_size(self, verdef_count: usize, verdaux_count: usize) -> usize { - verdef_count * mem::size_of::>() - + verdaux_count * mem::size_of::>() - } - - /// Return the size of a GNU version dependency section. - pub fn gnu_verneed_size(self, verneed_count: usize, vernaux_count: usize) -> usize { - verneed_count * mem::size_of::>() - + vernaux_count * mem::size_of::>() - } -} - -/// Native endian version of [`elf::FileHeader64`]. -#[allow(missing_docs)] -#[derive(Debug, Clone, Default)] -pub struct FileHeader { - pub os_abi: elf::OsAbi, - pub abi_version: u8, - pub e_type: elf::FileType, - pub e_machine: elf::Machine, - pub e_entry: u64, - pub e_flags: elf::FileFlags, -} - -#[derive(Debug, Clone, Default)] -struct FileHeaderLayout { - segment_offset: u64, - segment_num: u32, - section_offset: u64, - section_num: u32, - shstrtab_index: SectionIndex, -} - -/// Native endian version of [`elf::ProgramHeader64`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct ProgramHeader { - pub p_type: elf::ProgramType, - pub p_flags: elf::ProgramFlags, - pub p_offset: u64, - pub p_vaddr: u64, - pub p_paddr: u64, - pub p_filesz: u64, - pub p_memsz: u64, - pub p_align: u64, -} - -/// Native endian version of [`elf::SectionHeader64`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct SectionHeader { - pub name: Option, - pub sh_type: elf::SectionType, - pub sh_flags: elf::SectionFlags, - pub sh_addr: u64, - pub sh_offset: u64, - pub sh_size: u64, - pub sh_link: u32, - pub sh_info: u32, - pub sh_addralign: u64, - pub sh_entsize: u64, -} - -/// Native endian version of [`elf::Sym64`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct Sym { - pub name: Option, - pub section: Option, - pub st_info: elf::SymbolInfo, - pub st_other: elf::SymbolOther, - pub st_shndx: elf::SymbolSection, - pub st_value: u64, - pub st_size: u64, -} - -/// Unified native endian version of [`elf::Rel64`] and [`elf::Rela64`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct Rel { - pub r_offset: u64, - pub r_sym: u32, - pub r_type: u32, - pub r_addend: i64, -} - -/// Information required for writing [`elf::Verdef`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct Verdef { - pub version: u16, - pub flags: elf::VersionFlags, - pub index: elf::VersionIndex, - pub aux_count: u16, - /// The name for the first [`elf::Verdaux`] entry. - pub name: StringId, -} - -/// Information required for writing [`elf::Verneed`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct Verneed { - pub version: u16, - pub aux_count: u16, - pub file: StringId, -} - -/// Information required for writing [`elf::Vernaux`]. -#[allow(missing_docs)] -#[derive(Debug, Clone)] -pub struct Vernaux { - pub flags: elf::VersionFlags, - pub index: elf::VersionIndex, - pub name: StringId, -} diff --git a/src/write/string.rs b/src/write/string.rs index d3c954f..39d1dc8 100644 --- a/src/write/string.rs +++ b/src/write/string.rs @@ -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) -> 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. /// diff --git a/src/write/util.rs b/src/write/util.rs index 9247a58..dc99a5b 100644 --- a/src/write/util.rs +++ b/src/write/util.rs @@ -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(buffer: &mut W, size: usize) { let new_len = align(buffer.len(), size); buffer.resize(new_len); } +#[allow(dead_code)] +pub(crate) fn write_pod(buffer: &mut W, val: &T) { + buffer.write_bytes(bytes_of(val)) +} + +#[allow(dead_code)] +pub(crate) fn write_pod_slice(buffer: &mut W, val: &[T]) { + buffer.write_bytes(bytes_of_slice(val)) +} + #[cfg(test)] mod tests { use super::*;