diff --git a/crates/examples/src/bin/elfcopy.rs b/crates/examples/src/bin/elfcopy.rs index c2518f81..16a4e4b8 100644 --- a/crates/examples/src/bin/elfcopy.rs +++ b/crates/examples/src/bin/elfcopy.rs @@ -1,14 +1,8 @@ -use std::convert::TryInto; -use std::error::Error; -use std::{env, fs, process}; - -use object::elf; -use object::read::elf::{Dyn, FileHeader, ProgramHeader, Rel, Rela, SectionHeader, Sym}; -use object::Endianness; - use std::collections::HashMap; +use std::error::Error; use std::fs::File; use std::io::{self, BufRead}; +use std::{env, fs, process}; fn main() { let mut args = env::args(); @@ -53,57 +47,24 @@ fn main() { }; let in_data = &*in_data; - let kind = match object::FileKind::parse(in_data) { - Ok(file) => file, - Err(err) => { - eprintln!("Failed to parse file: {}", err); - process::exit(1); - } - }; - let out_data = match kind { - object::FileKind::Elf32 => { - copy_file::>(in_data, redefine_file).unwrap() - } - object::FileKind::Elf64 => { - copy_file::>(in_data, redefine_file).unwrap() - } - _ => { - eprintln!("Not an ELF file"); - process::exit(1); - } - }; + let mut builder = object::write::elf::Builder::read(in_data).unwrap(); + + let redefine_table = RedefineSymTable::new(redefine_file).unwrap(); + for symbol in &mut builder.dynamic_symbols { + symbol.name = redefine_table.get_redefined_name(symbol.name); + } + for symbol in &mut builder.symbols { + symbol.name = redefine_table.get_redefined_name(symbol.name); + } + + let mut out_data = Vec::new(); + builder.write(&mut out_data).unwrap(); if let Err(err) = fs::write(&out_file_path, out_data) { eprintln!("Failed to write file '{}': {}", out_file_path, err); process::exit(1); } } -struct Section { - name: Option, - offset: usize, -} - -struct Dynamic { - tag: u32, - // Ignored if `string` is set. - val: u64, - string: Option, -} - -struct Symbol { - in_sym: usize, - name: Option, - section: Option, -} - -struct DynamicSymbol { - in_sym: usize, - name: Option, - section: Option, - hash: Option, - gnu_hash: Option, -} - /// Table that holds a map of the symbols we should rename while copying /// /// This will be loaded by passing a file with lines of the form: @@ -157,847 +118,3 @@ impl RedefineSymTable { original } } - -fn copy_file>( - in_data: &[u8], - redefine_file: Option, -) -> Result, Box> { - let in_elf = Elf::parse(in_data)?; - let endian = in_elf.endian()?; - let is_mips64el = in_elf.is_mips64el(endian); - let in_segments = in_elf.program_headers(endian, in_data)?; - let in_sections = in_elf.sections(endian, in_data)?; - let in_syms = in_sections.symbols(endian, in_data, elf::SHT_SYMTAB)?; - let in_dynsyms = in_sections.symbols(endian, in_data, elf::SHT_DYNSYM)?; - - let redefine_table = RedefineSymTable::new(redefine_file)?; - - let mut out_data = Vec::new(); - let mut writer = object::write::elf::Writer::new(endian, in_elf.is_class_64(), &mut out_data); - - // Find metadata sections, and assign section indices. - let mut in_dynamic = None; - let mut in_hash = None; - let mut in_gnu_hash = None; - let mut in_versym = None; - let mut in_verdef = None; - let mut in_verneed = None; - let mut in_attributes = None; - let mut out_sections = Vec::with_capacity(in_sections.len()); - let mut out_sections_index = Vec::with_capacity(in_sections.len()); - for (i, in_section) in in_sections.iter().enumerate() { - let mut name = None; - let index; - match in_section.sh_type(endian) { - elf::SHT_NULL => { - index = writer.reserve_null_section_index(); - } - elf::SHT_PROGBITS - | elf::SHT_NOBITS - | elf::SHT_NOTE - | elf::SHT_REL - | elf::SHT_RELA - | elf::SHT_INIT_ARRAY - | elf::SHT_FINI_ARRAY => { - name = Some(writer.add_section_name(in_sections.section_name(endian, in_section)?)); - index = writer.reserve_section_index(); - } - elf::SHT_STRTAB => { - if i == in_syms.string_section().0 { - index = writer.reserve_strtab_section_index(); - } else if i == in_dynsyms.string_section().0 { - index = writer.reserve_dynstr_section_index(); - } else if i == in_elf.shstrndx(endian, in_data)? as usize { - index = writer.reserve_shstrtab_section_index(); - } else { - panic!("Unsupported string section {}", i); - } - } - elf::SHT_SYMTAB => { - if i == in_syms.section().0 { - index = writer.reserve_symtab_section_index(); - } else { - panic!("Unsupported symtab section {}", i); - } - } - elf::SHT_SYMTAB_SHNDX => { - if i == in_syms.shndx_section().0 { - index = writer.reserve_symtab_shndx_section_index(); - } else { - panic!("Unsupported symtab shndx section {}", i); - } - } - elf::SHT_DYNSYM => { - if i == in_dynsyms.section().0 { - index = writer.reserve_dynsym_section_index(); - } else { - panic!("Unsupported dynsym section {}", i); - } - } - elf::SHT_DYNAMIC => { - assert!(in_dynamic.is_none()); - in_dynamic = in_section.dynamic(endian, in_data)?; - debug_assert!(in_dynamic.is_some()); - index = writer.reserve_dynamic_section_index(); - } - elf::SHT_HASH => { - assert!(in_hash.is_none()); - in_hash = in_section.hash_header(endian, in_data)?; - debug_assert!(in_hash.is_some()); - index = writer.reserve_hash_section_index(); - } - elf::SHT_GNU_HASH => { - assert!(in_gnu_hash.is_none()); - in_gnu_hash = in_section.gnu_hash_header(endian, in_data)?; - debug_assert!(in_gnu_hash.is_some()); - index = writer.reserve_gnu_hash_section_index(); - } - elf::SHT_GNU_VERSYM => { - in_versym = in_section.gnu_versym(endian, in_data)?; - debug_assert!(in_versym.is_some()); - index = writer.reserve_gnu_versym_section_index(); - } - elf::SHT_GNU_VERDEF => { - in_verdef = in_section.gnu_verdef(endian, in_data)?; - debug_assert!(in_verdef.is_some()); - index = writer.reserve_gnu_verdef_section_index(); - } - elf::SHT_GNU_VERNEED => { - in_verneed = in_section.gnu_verneed(endian, in_data)?; - debug_assert!(in_verneed.is_some()); - index = writer.reserve_gnu_verneed_section_index(); - } - elf::SHT_GNU_ATTRIBUTES => { - in_attributes = in_section.gnu_attributes(endian, in_data)?; - debug_assert!(in_attributes.is_some()); - index = writer.reserve_gnu_attributes_section_index(); - } - other => { - panic!("Unsupported section type {:x}", other); - } - } - out_sections.push(Section { name, offset: 0 }); - out_sections_index.push(index); - } - - // Assign dynamic strings. - let mut out_dynamic = Vec::new(); - if let Some((in_dynamic, link)) = in_dynamic { - out_dynamic.reserve(in_dynamic.len()); - let in_dynamic_strings = in_sections.strings(endian, in_data, link)?; - for d in in_dynamic { - let tag = d.d_tag(endian).into().try_into()?; - let val = d.d_val(endian).into(); - let string = if d.is_string(endian) { - let s = in_dynamic_strings - .get(val.try_into()?) - .map_err(|_| "Invalid dynamic string")?; - Some(writer.add_dynamic_string(s)) - } else { - None - }; - out_dynamic.push(Dynamic { tag, val, string }); - if tag == elf::DT_NULL { - break; - } - } - } - - // Assign dynamic symbol indices. - let mut out_dynsyms = Vec::with_capacity(in_dynsyms.len()); - for (i, in_dynsym) in in_dynsyms.iter().enumerate().skip(1) { - let section = match in_dynsyms.symbol_section(endian, in_dynsym, i)? { - Some(in_section) => { - // Skip symbols for sections we aren't copying. - if out_sections_index[in_section.0].0 == 0 { - continue; - } - Some(out_sections_index[in_section.0]) - } - None => None, - }; - let mut name = None; - let mut hash = None; - let mut gnu_hash = None; - if in_dynsym.st_name(endian) != 0 { - let in_name = in_dynsyms.symbol_name(endian, in_dynsym)?; - let redefined_name = redefine_table.get_redefined_name(in_name); - name = Some(writer.add_dynamic_string(redefined_name)); - if !redefined_name.is_empty() { - hash = Some(elf::hash(redefined_name)); - if !in_dynsym.is_undefined(endian) { - gnu_hash = Some(elf::gnu_hash(redefined_name)); - } - } - }; - out_dynsyms.push(DynamicSymbol { - in_sym: i, - name, - section, - hash, - gnu_hash, - }); - } - // We must sort for GNU hash before allocating symbol indices. - if let Some(in_gnu_hash) = in_gnu_hash.as_ref() { - // TODO: recalculate bucket_count - out_dynsyms.sort_by_key(|sym| match sym.gnu_hash { - None => (0, 0), - Some(hash) => (1, hash % in_gnu_hash.bucket_count.get(endian)), - }); - } - let mut out_dynsyms_index = vec![Default::default(); in_dynsyms.len()]; - for out_dynsym in out_dynsyms.iter_mut() { - out_dynsyms_index[out_dynsym.in_sym] = writer.reserve_dynamic_symbol_index(); - } - - // Hash parameters. - let hash_index_base = out_dynsyms - .first() - .map(|sym| out_dynsyms_index[sym.in_sym].0) - .unwrap_or(0); - let hash_chain_count = writer.dynamic_symbol_count(); - - // GNU hash parameters. - let gnu_hash_index_base = out_dynsyms - .iter() - .position(|sym| sym.gnu_hash.is_some()) - .unwrap_or(0); - let gnu_hash_symbol_base = out_dynsyms - .iter() - .find(|sym| sym.gnu_hash.is_some()) - .map(|sym| out_dynsyms_index[sym.in_sym].0) - .unwrap_or_else(|| writer.dynamic_symbol_count()); - let gnu_hash_symbol_count = writer.dynamic_symbol_count() - gnu_hash_symbol_base; - - // Assign symbol indices. - let mut num_local = 0; - let mut out_syms = Vec::with_capacity(in_syms.len()); - let mut out_syms_index = Vec::with_capacity(in_syms.len()); - out_syms_index.push(Default::default()); - for (i, in_sym) in in_syms.iter().enumerate().skip(1) { - let section = match in_syms.symbol_section(endian, in_sym, i)? { - Some(in_section) => { - // Skip symbols for sections we aren't copying. - if out_sections_index[in_section.0].0 == 0 { - out_syms_index.push(Default::default()); - continue; - } - Some(out_sections_index[in_section.0]) - } - None => None, - }; - out_syms_index.push(writer.reserve_symbol_index(section)); - let name = if in_sym.st_name(endian) != 0 { - Some(writer.add_string( - redefine_table.get_redefined_name(in_syms.symbol_name(endian, in_sym)?), - )) - } else { - None - }; - out_syms.push(Symbol { - in_sym: i, - name, - section, - }); - if in_sym.st_bind() == elf::STB_LOCAL { - num_local = writer.symbol_count(); - } - } - - // Symbol version parameters. - let mut verdef_count = 0; - let mut verdaux_count = 0; - if let Some((mut verdefs, link)) = in_verdef.clone() { - let strings = in_sections.strings(endian, in_data, link)?; - while let Some((verdef, mut verdauxs)) = verdefs.next()? { - assert!(verdef.vd_cnt.get(endian) > 0); - verdef_count += 1; - while let Some(verdaux) = verdauxs.next()? { - writer.add_dynamic_string(verdaux.name(endian, strings)?); - verdaux_count += 1; - } - } - } - - let mut verneed_count = 0; - let mut vernaux_count = 0; - if let Some((mut verneeds, link)) = in_verneed.clone() { - let strings = in_sections.strings(endian, in_data, link)?; - while let Some((verneed, mut vernauxs)) = verneeds.next()? { - writer.add_dynamic_string(verneed.file(endian, strings)?); - verneed_count += 1; - while let Some(vernaux) = vernauxs.next()? { - writer.add_dynamic_string(vernaux.name(endian, strings)?); - vernaux_count += 1; - } - } - } - - let mut gnu_attributes = Vec::new(); - if let Some(attributes) = in_attributes { - let mut writer = writer.attributes_writer(); - let mut subsections = attributes.subsections()?; - while let Some(subsection) = subsections.next()? { - writer.start_subsection(subsection.vendor()); - let mut subsubsections = subsection.subsubsections(); - while let Some(subsubsection) = subsubsections.next()? { - writer.start_subsubsection(subsubsection.tag()); - match subsubsection.tag() { - elf::Tag_File => {} - elf::Tag_Section => { - let mut indices = subsubsection.indices(); - while let Some(index) = indices.next()? { - writer.write_subsubsection_index(out_sections_index[index as usize].0); - } - writer.write_subsubsection_index(0); - } - elf::Tag_Symbol => { - let mut indices = subsubsection.indices(); - while let Some(index) = indices.next()? { - writer.write_subsubsection_index(out_syms_index[index as usize].0); - } - writer.write_subsubsection_index(0); - } - _ => unimplemented!(), - } - writer.write_subsubsection_attributes(subsubsection.attributes_data()); - writer.end_subsubsection(); - } - writer.end_subsection(); - } - gnu_attributes = writer.data(); - assert_ne!(gnu_attributes.len(), 0); - } - - // Start reserving file ranges. - writer.reserve_file_header(); - - let mut hash_addr = 0; - let mut gnu_hash_addr = 0; - let mut versym_addr = 0; - let mut verdef_addr = 0; - let mut verneed_addr = 0; - let mut dynamic_addr = 0; - let mut dynsym_addr = 0; - let mut dynstr_addr = 0; - - let mut alloc_sections = Vec::new(); - if in_segments.is_empty() { - // Reserve sections at any offset. - for (i, in_section) in in_sections.iter().enumerate() { - match in_section.sh_type(endian) { - elf::SHT_PROGBITS | elf::SHT_NOTE | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => { - out_sections[i].offset = writer.reserve( - in_section.sh_size(endian).into() as usize, - in_section.sh_addralign(endian).into() as usize, - ); - } - elf::SHT_GNU_ATTRIBUTES => { - writer.reserve_gnu_attributes(gnu_attributes.len()); - } - _ => {} - } - } - } else { - // We don't support moving program headers. - assert_eq!(in_elf.e_phoff(endian).into(), writer.reserved_len() as u64); - writer.reserve_program_headers(in_segments.len() as u32); - - // Reserve alloc sections at original offsets. - alloc_sections = in_sections - .iter() - .enumerate() - .filter(|(_, s)| s.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0) - .collect(); - // The data for alloc sections may need to be written in a different order - // from their section headers. - alloc_sections.sort_by_key(|(_, x)| x.sh_offset(endian).into()); - for (i, in_section) in alloc_sections.iter() { - writer.reserve_until(in_section.sh_offset(endian).into() as usize); - match in_section.sh_type(endian) { - elf::SHT_PROGBITS | elf::SHT_NOTE | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => { - out_sections[*i].offset = - writer.reserve(in_section.sh_size(endian).into() as usize, 1); - } - elf::SHT_NOBITS => { - out_sections[*i].offset = writer.reserved_len(); - } - elf::SHT_REL => { - let (rels, _link) = in_section.rel(endian, in_data)?.unwrap(); - out_sections[*i].offset = writer.reserve_relocations(rels.len(), false); - } - elf::SHT_RELA => { - let (rels, _link) = in_section.rela(endian, in_data)?.unwrap(); - out_sections[*i].offset = writer.reserve_relocations(rels.len(), true); - } - elf::SHT_DYNAMIC => { - dynamic_addr = in_section.sh_addr(endian).into(); - writer.reserve_dynamic(out_dynamic.len()); - } - elf::SHT_DYNSYM if *i == in_dynsyms.section().0 => { - dynsym_addr = in_section.sh_addr(endian).into(); - writer.reserve_dynsym(); - } - elf::SHT_STRTAB if *i == in_dynsyms.string_section().0 => { - dynstr_addr = in_section.sh_addr(endian).into(); - writer.reserve_dynstr(); - } - elf::SHT_HASH => { - hash_addr = in_section.sh_addr(endian).into(); - let hash = in_hash.as_ref().unwrap(); - writer.reserve_hash(hash.bucket_count.get(endian), hash_chain_count); - } - elf::SHT_GNU_HASH => { - gnu_hash_addr = in_section.sh_addr(endian).into(); - let hash = in_gnu_hash.as_ref().unwrap(); - writer.reserve_gnu_hash( - hash.bloom_count.get(endian), - hash.bucket_count.get(endian), - gnu_hash_symbol_count, - ); - } - elf::SHT_GNU_VERSYM => { - versym_addr = in_section.sh_addr(endian).into(); - writer.reserve_gnu_versym(); - } - elf::SHT_GNU_VERDEF => { - verdef_addr = in_section.sh_addr(endian).into(); - writer.reserve_gnu_verdef(verdef_count, verdaux_count); - } - elf::SHT_GNU_VERNEED => { - verneed_addr = in_section.sh_addr(endian).into(); - writer.reserve_gnu_verneed(verneed_count, vernaux_count); - } - other => { - panic!("Unsupported alloc section index {}, type {}", *i, other); - } - } - } - - // Reserve non-alloc sections at any offset. - for (i, in_section) in in_sections.iter().enumerate() { - if in_section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 { - continue; - } - match in_section.sh_type(endian) { - elf::SHT_PROGBITS | elf::SHT_NOTE => { - out_sections[i].offset = writer.reserve( - in_section.sh_size(endian).into() as usize, - in_section.sh_addralign(endian).into() as usize, - ); - } - elf::SHT_GNU_ATTRIBUTES => { - writer.reserve_gnu_attributes(gnu_attributes.len()); - } - _ => {} - } - } - } - - writer.reserve_symtab(); - writer.reserve_symtab_shndx(); - writer.reserve_strtab(); - - for (i, in_section) in in_sections.iter().enumerate() { - if !in_segments.is_empty() - && in_section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 - { - continue; - } - match in_section.sh_type(endian) { - elf::SHT_REL => { - let (rels, _link) = in_section.rel(endian, in_data)?.unwrap(); - out_sections[i].offset = writer.reserve_relocations(rels.len(), false); - } - elf::SHT_RELA => { - let (rels, _link) = in_section.rela(endian, in_data)?.unwrap(); - out_sections[i].offset = writer.reserve_relocations(rels.len(), true); - } - _ => {} - } - } - - writer.reserve_shstrtab(); - writer.reserve_section_headers(); - - writer.write_file_header(&object::write::elf::FileHeader { - os_abi: in_elf.e_ident().os_abi, - abi_version: in_elf.e_ident().abi_version, - e_type: in_elf.e_type(endian), - e_machine: in_elf.e_machine(endian), - e_entry: in_elf.e_entry(endian).into(), - e_flags: in_elf.e_flags(endian), - })?; - - if in_segments.is_empty() { - for (i, in_section) in in_sections.iter().enumerate() { - match in_section.sh_type(endian) { - elf::SHT_PROGBITS | elf::SHT_NOTE | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => { - writer.write_align(in_section.sh_addralign(endian).into() as usize); - debug_assert_eq!(out_sections[i].offset, writer.len()); - writer.write(in_section.data(endian, in_data)?); - } - elf::SHT_GNU_ATTRIBUTES => { - writer.write_gnu_attributes(&gnu_attributes); - } - _ => {} - } - } - } else { - writer.write_align_program_headers(); - for in_segment in in_segments { - writer.write_program_header(&object::write::elf::ProgramHeader { - p_type: in_segment.p_type(endian), - p_flags: in_segment.p_flags(endian), - p_offset: in_segment.p_offset(endian).into(), - p_vaddr: in_segment.p_vaddr(endian).into(), - p_paddr: in_segment.p_paddr(endian).into(), - p_filesz: in_segment.p_filesz(endian).into(), - p_memsz: in_segment.p_memsz(endian).into(), - p_align: in_segment.p_align(endian).into(), - }); - } - - for (i, in_section) in alloc_sections.iter() { - writer.pad_until(in_section.sh_offset(endian).into() as usize); - match in_section.sh_type(endian) { - elf::SHT_PROGBITS | elf::SHT_NOTE | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => { - debug_assert_eq!(out_sections[*i].offset, writer.len()); - writer.write(in_section.data(endian, in_data)?); - } - elf::SHT_NOBITS => {} - elf::SHT_REL => { - let (rels, _link) = in_section.rel(endian, in_data)?.unwrap(); - writer.write_align_relocation(); - for rel in rels { - let in_sym = rel.r_sym(endian); - let out_sym = if in_sym != 0 { - out_dynsyms_index[in_sym as usize].0 - } else { - 0 - }; - writer.write_relocation( - false, - &object::write::elf::Rel { - r_offset: rel.r_offset(endian).into(), - r_sym: out_sym, - r_type: rel.r_type(endian), - r_addend: 0, - }, - ); - } - } - elf::SHT_RELA => { - let (rels, _link) = in_section.rela(endian, in_data)?.unwrap(); - writer.write_align_relocation(); - for rel in rels { - let in_sym = rel.r_sym(endian, is_mips64el); - let out_sym = if in_sym != 0 { - out_dynsyms_index[in_sym as usize].0 - } else { - 0 - }; - writer.write_relocation( - true, - &object::write::elf::Rel { - r_offset: rel.r_offset(endian).into(), - r_sym: out_sym, - r_type: rel.r_type(endian, is_mips64el), - r_addend: rel.r_addend(endian).into(), - }, - ); - } - } - elf::SHT_DYNAMIC => { - for d in &out_dynamic { - if let Some(string) = d.string { - writer.write_dynamic_string(d.tag, string); - } else { - // TODO: fix values - let val = d.val; - writer.write_dynamic(d.tag, val); - } - } - } - elf::SHT_DYNSYM if *i == in_dynsyms.section().0 => { - writer.write_null_dynamic_symbol(); - 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, - st_info: in_dynsym.st_info(), - st_other: in_dynsym.st_other(), - st_shndx: in_dynsym.st_shndx(endian), - st_value: in_dynsym.st_value(endian).into(), - st_size: in_dynsym.st_size(endian).into(), - }); - } - } - elf::SHT_STRTAB if *i == in_dynsyms.string_section().0 => { - writer.write_dynstr(); - } - elf::SHT_HASH => { - let hash = in_hash.as_ref().unwrap(); - writer.write_hash(hash.bucket_count.get(endian), hash_chain_count, |index| { - out_dynsyms - .get(index.checked_sub(hash_index_base)? as usize)? - .hash - }); - } - elf::SHT_GNU_HASH => { - let gnu_hash = in_gnu_hash.as_ref().unwrap(); - writer.write_gnu_hash( - gnu_hash_symbol_base, - gnu_hash.bloom_shift.get(endian), - gnu_hash.bloom_count.get(endian), - gnu_hash.bucket_count.get(endian), - gnu_hash_symbol_count, - |index| { - out_dynsyms[gnu_hash_index_base + index as usize] - .gnu_hash - .unwrap() - }, - ); - } - elf::SHT_GNU_VERSYM => { - let (in_versym, _) = in_versym.as_ref().unwrap(); - writer.write_null_gnu_versym(); - for out_dynsym in &out_dynsyms { - writer.write_gnu_versym( - in_versym.get(out_dynsym.in_sym).unwrap().0.get(endian), - ); - } - } - elf::SHT_GNU_VERDEF => { - let (mut verdefs, link) = in_verdef.clone().unwrap(); - let strings = in_sections.strings(endian, in_data, link)?; - writer.write_align_gnu_verdef(); - while let Some((verdef, mut verdauxs)) = verdefs.next()? { - let verdaux = verdauxs.next()?.unwrap(); - writer.write_gnu_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)?), - }); - while let Some(verdaux) = verdauxs.next()? { - writer.write_gnu_verdaux( - writer.get_dynamic_string(verdaux.name(endian, strings)?), - ); - } - } - } - elf::SHT_GNU_VERNEED => { - let (mut verneeds, link) = in_verneed.clone().unwrap(); - let strings = in_sections.strings(endian, in_data, link)?; - writer.write_align_gnu_verneed(); - while let Some((verneed, mut vernauxs)) = verneeds.next()? { - 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)?), - }); - while let Some(vernaux) = vernauxs.next()? { - 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)?), - }); - } - } - } - other => { - panic!("Unsupported alloc section type {:x}", other); - } - } - } - - for (i, in_section) in in_sections.iter().enumerate() { - if in_section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 { - continue; - } - match in_section.sh_type(endian) { - elf::SHT_PROGBITS | elf::SHT_NOTE => { - writer.write_align(in_section.sh_addralign(endian).into() as usize); - debug_assert_eq!(out_sections[i].offset, writer.len()); - writer.write(in_section.data(endian, in_data)?); - } - elf::SHT_GNU_ATTRIBUTES => { - writer.write_gnu_attributes(&gnu_attributes); - } - _ => {} - } - } - } - - writer.write_null_symbol(); - 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, - st_info: in_sym.st_info(), - st_other: in_sym.st_other(), - st_shndx: in_sym.st_shndx(endian), - st_value: in_sym.st_value(endian).into(), - st_size: in_sym.st_size(endian).into(), - }); - } - writer.write_symtab_shndx(); - writer.write_strtab(); - - for in_section in in_sections.iter() { - if !in_segments.is_empty() - && in_section.sh_flags(endian).into() & u64::from(elf::SHF_ALLOC) != 0 - { - continue; - } - let out_syms = if in_section.sh_link(endian) as usize == in_syms.section().0 { - &out_syms_index - } else { - &out_dynsyms_index - }; - match in_section.sh_type(endian) { - elf::SHT_REL => { - let (rels, _link) = in_section.rel(endian, in_data)?.unwrap(); - writer.write_align_relocation(); - for rel in rels { - let in_sym = rel.r_sym(endian); - let out_sym = if in_sym != 0 { - out_syms[in_sym as usize].0 - } else { - 0 - }; - writer.write_relocation( - false, - &object::write::elf::Rel { - r_offset: rel.r_offset(endian).into(), - r_sym: out_sym, - r_type: rel.r_type(endian), - r_addend: 0, - }, - ); - } - } - elf::SHT_RELA => { - let (rels, _link) = in_section.rela(endian, in_data)?.unwrap(); - writer.write_align_relocation(); - for rel in rels { - let in_sym = rel.r_sym(endian, is_mips64el); - let out_sym = if in_sym != 0 { - out_syms[in_sym as usize].0 - } else { - 0 - }; - writer.write_relocation( - true, - &object::write::elf::Rel { - r_offset: rel.r_offset(endian).into(), - r_sym: out_sym, - r_type: rel.r_type(endian, is_mips64el), - r_addend: rel.r_addend(endian).into(), - }, - ); - } - } - _ => {} - } - } - - writer.write_shstrtab(); - - writer.write_null_section_header(); - for (i, in_section) in in_sections.iter().enumerate() { - match in_section.sh_type(endian) { - elf::SHT_NULL => {} - elf::SHT_PROGBITS - | elf::SHT_NOBITS - | elf::SHT_NOTE - | elf::SHT_REL - | elf::SHT_RELA - | elf::SHT_INIT_ARRAY - | elf::SHT_FINI_ARRAY => { - let out_section = &out_sections[i]; - let sh_link = out_sections_index[in_section.sh_link(endian) as usize].0; - let mut sh_info = in_section.sh_info(endian); - if in_section.sh_flags(endian).into() as u32 & elf::SHF_INFO_LINK != 0 { - sh_info = out_sections_index[sh_info as usize].0; - } - writer.write_section_header(&object::write::elf::SectionHeader { - name: out_section.name, - sh_type: in_section.sh_type(endian), - sh_flags: in_section.sh_flags(endian).into(), - sh_addr: in_section.sh_addr(endian).into(), - sh_offset: out_section.offset as u64, - sh_size: in_section.sh_size(endian).into(), - sh_link, - sh_info, - sh_addralign: in_section.sh_addralign(endian).into(), - sh_entsize: in_section.sh_entsize(endian).into(), - }); - } - elf::SHT_STRTAB => { - if i == in_syms.string_section().0 { - writer.write_strtab_section_header(); - } else if i == in_dynsyms.string_section().0 { - writer.write_dynstr_section_header(dynstr_addr); - } else if i == in_elf.shstrndx(endian, in_data)? as usize { - writer.write_shstrtab_section_header(); - } else { - panic!("Unsupported string section {}", i); - } - } - elf::SHT_SYMTAB => { - if i == in_syms.section().0 { - writer.write_symtab_section_header(num_local); - } else { - panic!("Unsupported symtab section {}", i); - } - } - elf::SHT_SYMTAB_SHNDX => { - if i == in_syms.shndx_section().0 { - writer.write_symtab_shndx_section_header(); - } else { - panic!("Unsupported symtab shndx section {}", i); - } - } - elf::SHT_DYNSYM => { - if i == in_dynsyms.section().0 { - writer.write_dynsym_section_header(dynsym_addr, 1); - } else { - panic!("Unsupported dynsym section {}", i); - } - } - elf::SHT_DYNAMIC => { - writer.write_dynamic_section_header(dynamic_addr); - } - elf::SHT_HASH => { - writer.write_hash_section_header(hash_addr); - } - elf::SHT_GNU_HASH => { - writer.write_gnu_hash_section_header(gnu_hash_addr); - } - elf::SHT_GNU_VERSYM => { - writer.write_gnu_versym_section_header(versym_addr); - } - elf::SHT_GNU_VERDEF => { - writer.write_gnu_verdef_section_header(verdef_addr); - } - elf::SHT_GNU_VERNEED => { - writer.write_gnu_verneed_section_header(verneed_addr); - } - elf::SHT_GNU_ATTRIBUTES => { - writer.write_gnu_attributes_section_header(); - } - other => { - panic!("Unsupported section type {:x}", other); - } - } - } - debug_assert_eq!(writer.reserved_len(), writer.len()); - - Ok(out_data) -} diff --git a/src/read/mod.rs b/src/read/mod.rs index c13e3091..364ec3ec 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -107,7 +107,7 @@ mod private { /// The error type used within the read module. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct Error(&'static str); +pub struct Error(pub(crate) &'static str); impl fmt::Display for Error { #[inline] diff --git a/src/write/elf/builder.rs b/src/write/elf/builder.rs new file mode 100644 index 00000000..8c5b2b3e --- /dev/null +++ b/src/write/elf/builder.rs @@ -0,0 +1,1703 @@ +#![allow(missing_docs)] + +use crate::elf; +use crate::write::{self, Error, Result, WritableBuffer}; +use crate::Endianness; +use alloc::vec::Vec; +use core::marker::PhantomData; + +#[cfg(feature = "read")] +use crate::read::elf::{Dyn, FileHeader, ProgramHeader, Rel, Rela, SectionHeader, Sym}; +#[cfg(feature = "read")] +use crate::read::{self, FileKind, ReadRef}; +#[cfg(feature = "read")] +use core::convert::TryInto; + +#[cfg(feature = "read")] +impl From for Error { + fn from(error: read::Error) -> Error { + Error(format!("{}", error)) + } +} + +#[derive(Debug)] +pub struct Builder<'data> { + pub endian: Endianness, + pub is_64: bool, + pub os_abi: u8, + pub abi_version: u8, + pub e_type: u16, + pub e_machine: u16, + pub e_entry: u64, + pub e_flags: u32, + pub e_phoff: usize, + + pub section_names: Option, + // TODO: use Cow for data references + pub segments: Segments<'data>, + pub sections: Sections<'data>, + pub symbols: Symbols<'data>, + pub dynamic_symbols: Symbols<'data>, + pub dynamics: Vec>, + pub version_base: Option<&'data [u8]>, + pub versions: Versions<'data>, + pub version_files: VersionFiles<'data>, + pub hash_bucket_count: u32, + pub gnu_hash_bloom_shift: u32, + pub gnu_hash_bloom_count: u32, + pub gnu_hash_bucket_count: u32, + pub gnu_attributes: AttributesSection<'data>, +} + +impl<'data> Builder<'data> { + #[cfg(feature = "read")] + pub fn read>(data: R) -> Result { + let kind = FileKind::parse(data)?; + match kind { + FileKind::Elf32 => Self::read_file::, R>(data), + FileKind::Elf64 => Self::read_file::, R>(data), + _ => Err(Error(format!("Not an ELF file"))), + } + } + + #[cfg(feature = "read")] + fn read_file(data: R) -> Result + where + Elf: FileHeader, + R: ReadRef<'data>, + { + let header = Elf::parse(data)?; + let endian = header.endian()?; + let is_mips64el = header.is_mips64el(endian); + let shstrndx = header.shstrndx(endian, data)? as usize; + let segments = header.program_headers(endian, data)?; + let sections = header.sections(endian, data)?; + let symbols = sections.symbols(endian, data, elf::SHT_SYMTAB)?; + let dynamic_symbols = sections.symbols(endian, data, elf::SHT_DYNSYM)?; + let gnu_versym = sections.gnu_versym(endian, data)?; + let gnu_verdef = sections.gnu_verdef(endian, data)?; + let gnu_verneed = sections.gnu_verneed(endian, data)?; + + let mut builder = Builder { + endian, + is_64: header.is_type_64(), + os_abi: header.e_ident().os_abi, + abi_version: header.e_ident().abi_version, + e_type: header.e_type(endian), + e_machine: header.e_machine(endian), + e_entry: header.e_entry(endian).into(), + e_flags: header.e_flags(endian), + e_phoff: header.e_phoff(endian).into() as usize, + + section_names: None, + segments: Segments::new(), + sections: Sections::new(), + symbols: Symbols::new(), + dynamic_symbols: Symbols::new(), + dynamics: Vec::new(), + version_base: None, + versions: Versions::new(), + version_files: VersionFiles::new(), + hash_bucket_count: 0, + gnu_hash_bloom_shift: 0, + gnu_hash_bloom_count: 0, + gnu_hash_bucket_count: 0, + gnu_attributes: AttributesSection::new(), + }; + + for segment in segments { + let id = SegmentId(builder.segments.0.len()); + builder.segments.0.push(Segment { + id, + p_type: segment.p_type(endian), + p_flags: segment.p_flags(endian), + p_offset: segment.p_offset(endian).into(), + p_vaddr: segment.p_vaddr(endian).into(), + p_paddr: segment.p_paddr(endian).into(), + p_filesz: segment.p_filesz(endian).into(), + p_memsz: segment.p_memsz(endian).into(), + p_align: segment.p_align(endian).into(), + delete: false, + marker: PhantomData, + }); + } + + for (index, section) in sections.iter().enumerate().skip(1) { + let id = SectionId(builder.sections.0.len()); + let relocations = if let Some((rels, _link)) = section.rel(endian, data)? { + rels.iter() + .map(|rel| { + let r_sym = rel.r_sym(endian); + let symbol = if r_sym == 0 { + None + } else { + Some(SymbolId(r_sym as usize - 1)) + }; + Relocation { + r_offset: rel.r_offset(endian).into(), + symbol, + r_type: rel.r_type(endian), + r_addend: 0, + delete: false, + } + }) + .collect() + } else if let Some((rels, _link)) = section.rela(endian, data)? { + rels.iter() + .map(|rel| { + let r_sym = rel.r_sym(endian, is_mips64el); + let symbol = if r_sym == 0 { + None + } else { + Some(SymbolId(r_sym as usize - 1)) + }; + Relocation { + r_offset: rel.r_offset(endian).into(), + symbol, + r_type: rel.r_type(endian, is_mips64el), + r_addend: rel.r_addend(endian).into(), + delete: false, + } + }) + .collect() + } else { + Vec::new() + }; + if let Some((dyns, link)) = section.dynamic(endian, data)? { + builder.dynamics.reserve(dyns.len()); + let dynamic_strings = sections.strings(endian, data, link)?; + for d in dyns { + let tag = d.d_tag(endian).into().try_into().map_err(|_| { + Error(format!( + "Unsupported dynamic tag 0x{:x}", + d.d_tag(endian).into() + )) + })?; + let val = d.d_val(endian).into(); + builder.dynamics.push(if d.is_string(endian) { + let val = dynamic_strings + .get(val.try_into().map_err(|_| { + Error(format!("Unsupported dynamic string 0x{:x}", val)) + })?) + .map_err(|_| Error(format!("Invalid dynamic string 0x{:x}", val)))?; + Dynamic::String { tag, val } + } else { + Dynamic::Integer { tag, val } + }); + if tag == elf::DT_NULL { + break; + } + } + } + if let Some(hash) = section.hash_header(endian, data)? { + builder.hash_bucket_count = hash.bucket_count.get(endian); + } + if let Some(hash) = section.gnu_hash_header(endian, data)? { + builder.gnu_hash_bloom_shift = hash.bloom_shift.get(endian); + builder.gnu_hash_bloom_count = hash.bloom_count.get(endian); + builder.gnu_hash_bucket_count = hash.bucket_count.get(endian); + } + if let Some(attributes) = section.gnu_attributes(endian, data)? { + let mut subsections = attributes.subsections()?; + while let Some(subsection) = subsections.next()? { + let mut builder_subsection = AttributesSubsection::new(subsection.vendor()); + let mut subsubsections = subsection.subsubsections(); + while let Some(subsubsection) = subsubsections.next()? { + let tag = match subsubsection.tag() { + elf::Tag_File => AttributeTag::File, + elf::Tag_Section => { + let mut sections = Vec::new(); + let mut indices = subsubsection.indices(); + while let Some(index) = indices.next()? { + sections.push(SectionId(index as usize - 1)); + } + AttributeTag::Section(sections) + } + elf::Tag_Symbol => { + let mut symbols = Vec::new(); + let mut indices = subsubsection.indices(); + while let Some(index) = indices.next()? { + symbols.push(SymbolId(index as usize - 1)); + } + AttributeTag::Symbol(symbols) + } + // FIXME + _ => unimplemented!(), + }; + let data = subsubsection.attributes_data(); + builder_subsection + .subsubsections + .push(AttributesSubsubsection { tag, data }); + } + builder.gnu_attributes.subsections.push(builder_subsection); + } + } + let data = match section.sh_type(endian) { + elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()), + elf::SHT_PROGBITS | elf::SHT_NOTE | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => { + SectionData::Data(section.data(endian, data)?) + } + elf::SHT_REL | elf::SHT_RELA => SectionData::Relocation, + elf::SHT_SYMTAB => { + if index == symbols.section().0 { + SectionData::Symbol + } else { + return Err(Error(format!( + "Unsupported SHT_SYMTAB section at index {}", + index + ))); + } + } + elf::SHT_SYMTAB_SHNDX => { + if index == symbols.shndx_section().0 { + SectionData::SymbolSectionIndex + } else { + return Err(Error(format!( + "Unsupported SHT_SYMTAB_SHNDX section at index {}", + index + ))); + } + } + elf::SHT_DYNSYM => { + if index == dynamic_symbols.section().0 { + SectionData::DynamicSymbol + } else { + return Err(Error(format!( + "Unsupported SHT_DYNSYM section at index {}", + index + ))); + } + } + elf::SHT_STRTAB => { + if index == shstrndx { + builder.section_names = Some(id); + } + if index == symbols.string_section().0 { + SectionData::String + } else if index == dynamic_symbols.string_section().0 { + SectionData::DynamicString + } else if index == shstrndx { + SectionData::SectionString + } else { + return Err(Error(format!( + "Unsupported SHT_STRTAB section at index {}", + index + ))); + } + } + elf::SHT_DYNAMIC => SectionData::Dynamic, + elf::SHT_HASH => SectionData::Hash, + elf::SHT_GNU_HASH => SectionData::GnuHash, + elf::SHT_GNU_VERSYM => SectionData::GnuVersym, + elf::SHT_GNU_VERDEF => SectionData::GnuVerdef, + elf::SHT_GNU_VERNEED => SectionData::GnuVerneed, + elf::SHT_GNU_ATTRIBUTES => SectionData::GnuAttributes, + other => return Err(Error(format!("Unsupported section type {:x}", other))), + }; + let sh_flags = section.sh_flags(endian).into(); + let sh_link = section.sh_link(endian); + let sh_link_section = if sh_link == 0 { + None + } else { + Some(SectionId(sh_link as usize - 1)) + }; + let sh_info = section.sh_info(endian); + let sh_info_section = if sh_info == 0 || sh_flags & u64::from(elf::SHF_INFO_LINK) == 0 { + None + } else { + Some(SectionId(sh_info as usize - 1)) + }; + builder.sections.0.push(Section { + id, + // TODO + segment: None, + name: sections.section_name(endian, section)?, + sh_type: section.sh_type(endian), + sh_flags: section.sh_flags(endian).into(), + sh_addr: section.sh_addr(endian).into(), + sh_offset: section.sh_offset(endian).into() as usize, + sh_link_section, + sh_info, + sh_info_section, + sh_addralign: section.sh_addralign(endian).into() as usize, + sh_entsize: section.sh_entsize(endian).into(), + data, + // TODO + relocations, + delete: false, + }); + } + + for (id, symbol) in symbols.iter().skip(1).enumerate() { + builder.symbols.0.push(Symbol { + id: SymbolId(id), + name: symbols.symbol_name(endian, symbol)?, + // TODO + section: None, + st_info: symbol.st_info(), + st_other: symbol.st_other(), + st_shndx: symbol.st_shndx(endian), + st_value: symbol.st_value(endian).into(), + st_size: symbol.st_size(endian).into(), + version: VersionId::none(), + version_hidden: false, + delete: false, + }); + } + + for (id, symbol) in dynamic_symbols.iter().skip(1).enumerate() { + builder.dynamic_symbols.0.push(Symbol { + id: SymbolId(id), + name: dynamic_symbols.symbol_name(endian, symbol)?, + // TODO + section: None, + st_info: symbol.st_info(), + st_other: symbol.st_other(), + st_shndx: symbol.st_shndx(endian), + st_value: symbol.st_value(endian).into(), + st_size: symbol.st_size(endian).into(), + version: VersionId::global(), + version_hidden: false, + delete: false, + }); + } + + if let Some((versyms, link)) = gnu_versym { + if dynamic_symbols.section() != link || versyms.len() != dynamic_symbols.len() { + return Err(Error(format!("Invalid SHT_GNU_VERSYM section"))); + } + let strings = dynamic_symbols.strings(); + + // TODO: can we derive max_index from versyms instead? + let mut max_index = 0; + if let Some((mut verdefs, link)) = gnu_verdef.clone() { + if dynamic_symbols.string_section() != link { + return Err(Error(format!("Invalid SHT_GNU_VERDEF section"))); + } + while let Some((verdef, _)) = verdefs.next()? { + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + if let Some((mut verneeds, link)) = gnu_verneed.clone() { + if dynamic_symbols.string_section() != link { + return Err(Error(format!("Invalid SHT_GNU_VERNEED section"))); + } + while let Some((_, mut vernauxs)) = verneeds.next()? { + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if max_index < index { + max_index = index; + } + } + } + } + + let mut ids = vec![VersionId::none(); max_index as usize + 1]; + + if let Some((mut verdefs, _)) = gnu_verdef { + while let Some((verdef, mut verdauxs)) = verdefs.next()? { + let flags = verdef.vd_flags.get(endian); + if flags & elf::VER_FLG_BASE != 0 { + if flags != elf::VER_FLG_BASE + || verdef.vd_ndx.get(endian) != 1 + || verdef.vd_cnt.get(endian) != 1 + { + return Err(Error(format!( + "Unsupported VER_FLG_BASE in SHT_GNU_VERDEF" + ))); + } + if builder.version_base.is_some() { + return Err(Error(format!("Duplicate VER_FLG_BASE in SHT_GNU_VERDEF"))); + } + let verdaux = verdauxs.next()?.ok_or_else(|| { + Error(format!("Missing name for VER_FLG_BASE in SHT_GNU_VERDEF")) + })?; + builder.version_base = Some(verdaux.name(endian, strings)?); + continue; + } + + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + if ids[usize::from(index)] != VersionId::none() { + return Err(Error(format!("Duplicate SHT_GNU_VERDEF index {}", index))); + } + let id = VersionId(builder.versions.0.len() + 2); + ids[usize::from(index)] = id; + + let mut names = Vec::new(); + while let Some(verdaux) = verdauxs.next()? { + names.push(verdaux.name(endian, strings)?); + } + + builder + .versions + .0 + .push(Version::Def(VersionDef { id, flags, names })); + } + } + + if let Some((mut verneeds, _)) = gnu_verneed { + while let Some((verneed, mut vernauxs)) = verneeds.next()? { + let file = VersionFileId(builder.version_files.0.len()); + builder.version_files.0.push(VersionFile { + id: file, + name: verneed.file(endian, strings)?, + }); + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + if ids[usize::from(index)] != VersionId::none() { + return Err(Error(format!( + "Duplicate SHT_GNU_VERNEED index {}", + index + ))); + } + let id = VersionId(builder.versions.0.len() + 2); + ids[usize::from(index)] = id; + + builder.versions.0.push(Version::Need(VersionNeed { + id, + flags: vernaux.vna_flags.get(endian), + name: vernaux.name(endian, strings)?, + file, + })); + } + } + } + + for (id, versym) in versyms.iter().skip(1).enumerate() { + let index = versym.0.get(endian); + let symbol = &mut builder.dynamic_symbols.0[id]; + symbol.version = *ids + .get((index & elf::VERSYM_VERSION) as usize) + .ok_or_else(|| Error(format!("Invalid SHT_GNU_VERSYM index {:x}", index)))?; + symbol.version_hidden = index & elf::VERSYM_HIDDEN != 0; + } + } + + Ok(builder) + } + + pub fn write(self, buffer: &mut dyn WritableBuffer) -> Result<()> { + struct SectionOut { + id: SectionId, + name: Option, + offset: usize, + } + + struct DynamicOut { + string: Option, + } + + struct SymbolOut { + id: SymbolId, + name: Option, + } + + struct DynamicSymbolOut { + id: SymbolId, + name: Option, + hash: Option, + gnu_hash: Option, + } + + #[derive(Default, Clone)] + struct VersionFileOut { + versions: Vec, + } + + 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 dynamic_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 gnu_attributes_id = None; + let mut out_sections = Vec::with_capacity(self.sections.0.len()); + let mut out_sections_index = Vec::with_capacity(self.sections.0.len()); + if !self.sections.0.is_empty() { + writer.reserve_null_section_index(); + } + for section in &self.sections.0 { + if section.delete { + out_sections_index.push(None); + continue; + } + + let index = match §ion.data { + SectionData::Data(_) + | SectionData::UninitializedData(_) + | SectionData::Relocation => writer.reserve_section_index(), + SectionData::SectionString => { + if shstrtab_id.is_some() { + return Err(Error(format!("Multiple .shstrtab sections"))); + } + shstrtab_id = Some(section.id); + writer.reserve_shstrtab_section_index() + } + SectionData::Symbol => { + if symtab_id.is_some() { + return Err(Error(format!("Multiple .symtab sections"))); + } + symtab_id = Some(section.id); + writer.reserve_symtab_section_index() + } + SectionData::SymbolSectionIndex => { + if symtab_shndx_id.is_some() { + return Err(Error(format!("Multiple .symtab_shndx sections"))); + } + symtab_shndx_id = Some(section.id); + writer.reserve_symtab_shndx_section_index() + } + SectionData::String => { + if strtab_id.is_some() { + return Err(Error(format!("Multiple .strtab sections"))); + } + strtab_id = Some(section.id); + writer.reserve_strtab_section_index() + } + SectionData::Dynamic => { + if dynamic_id.is_some() { + return Err(Error(format!("Multiple .dynamic sections"))); + } + dynamic_id = Some(section.id); + writer.reserve_dynamic_section_index() + } + SectionData::DynamicSymbol => { + if dynsym_id.is_some() { + return Err(Error(format!("Multiple .dynsym sections"))); + } + dynsym_id = Some(section.id); + writer.reserve_dynsym_section_index() + } + SectionData::DynamicString => { + if dynstr_id.is_some() { + return Err(Error(format!("Multiple .dynstr sections"))); + } + dynstr_id = Some(section.id); + writer.reserve_dynstr_section_index() + } + SectionData::Hash => { + if hash_id.is_some() { + return Err(Error(format!("Multiple .hash sections"))); + } + hash_id = Some(section.id); + writer.reserve_hash_section_index() + } + SectionData::GnuHash => { + if gnu_hash_id.is_some() { + return Err(Error(format!("Multiple .gnu.hash sections"))); + } + gnu_hash_id = Some(section.id); + writer.reserve_gnu_hash_section_index() + } + SectionData::GnuVersym => { + if gnu_versym_id.is_some() { + return Err(Error(format!("Multiple .gnu.version sections"))); + } + gnu_versym_id = Some(section.id); + writer.reserve_gnu_versym_section_index() + } + SectionData::GnuVerdef => { + if gnu_verdef_id.is_some() { + return Err(Error(format!("Multiple .gnu.version_d sections"))); + } + gnu_verdef_id = Some(section.id); + writer.reserve_gnu_verdef_section_index() + } + SectionData::GnuVerneed => { + if gnu_verneed_id.is_some() { + return Err(Error(format!("Multiple .gnu.version_r sections"))); + } + gnu_verneed_id = Some(section.id); + writer.reserve_gnu_verneed_section_index() + } + SectionData::GnuAttributes => { + if gnu_attributes_id.is_some() { + return Err(Error(format!("Multiple .gnu.attributes sections"))); + } + gnu_attributes_id = Some(section.id); + writer.reserve_gnu_attributes_section_index() + } + }; + out_sections_index.push(Some(index)); + + let name = if section.name.is_empty() { + None + } else { + // TODO: this is ignored for special sections + Some(writer.add_section_name(section.name)) + }; + out_sections.push(SectionOut { + id: section.id, + name, + offset: 0, + }); + } + + // Assign dynamic strings. + let mut out_dynamics = Vec::new(); + if dynamic_id.is_some() { + out_dynamics.reserve(self.dynamics.len()); + for dynamic in &self.dynamics { + let string = match dynamic { + Dynamic::Integer { .. } => None, + Dynamic::String { val, .. } => Some(writer.add_dynamic_string(val)), + }; + out_dynamics.push(DynamicOut { string }); + } + } + + // Assign dynamic symbol indices. + let mut out_dynsyms = Vec::new(); + if dynsym_id.is_some() { + out_dynsyms.reserve(self.dynamic_symbols.0.len()); + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + // Skip symbols for deleted sections. + if let Some(section) = symbol.section { + if out_sections_index[section.0].is_none() { + continue; + } + } + + let mut name = None; + 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() { + hash = Some(elf::hash(symbol.name)); + } + if gnu_hash_id.is_some() && symbol.st_shndx != elf::SHN_UNDEF { + gnu_hash = Some(elf::gnu_hash(symbol.name)); + } + } + out_dynsyms.push(DynamicSymbolOut { + id: symbol.id, + name, + hash, + gnu_hash, + }); + } + } + // We must sort for GNU hash before allocating symbol indices. + if gnu_hash_id.is_some() { + if self.gnu_hash_bucket_count == 0 { + return Err(Error(format!(".gnu.hash bucket count is zero"))); + } + // TODO: recalculate bucket_count? + out_dynsyms.sort_by_key(|sym| match sym.gnu_hash { + None => (0, 0), + Some(hash) => (1, hash % self.gnu_hash_bucket_count), + }); + } + let mut out_dynsyms_index = vec![None; self.dynamic_symbols.0.len()]; + for out_dynsym in out_dynsyms.iter_mut() { + out_dynsyms_index[out_dynsym.id.0] = Some(writer.reserve_dynamic_symbol_index()); + } + + // Hash parameters. + let hash_index_base = out_dynsyms + .first() + .map(|sym| out_dynsyms_index[sym.id.0].unwrap().0) + .unwrap_or(0); + let hash_chain_count = writer.dynamic_symbol_count(); + + // GNU hash parameters. + let gnu_hash_index_base = out_dynsyms + .iter() + .position(|sym| sym.gnu_hash.is_some()) + .unwrap_or(0); + let gnu_hash_symbol_base = out_dynsyms + .iter() + .find(|sym| sym.gnu_hash.is_some()) + .map(|sym| out_dynsyms_index[sym.id.0].unwrap().0) + .unwrap_or_else(|| writer.dynamic_symbol_count()); + let gnu_hash_symbol_count = writer.dynamic_symbol_count() - gnu_hash_symbol_base; + + // Assign symbol indices. + let mut out_syms = Vec::with_capacity(self.symbols.0.len()); + if symtab_id.is_some() { + // Local symbols must come before global. + let local_symbols = self + .symbols + .into_iter() + .filter(|symbol| symbol.st_bind() == elf::STB_LOCAL); + let global_symbols = self + .symbols + .into_iter() + .filter(|symbol| symbol.st_bind() != elf::STB_LOCAL); + for symbol in local_symbols.chain(global_symbols) { + if symbol.delete { + continue; + } + // Skip symbols for deleted sections. + if let Some(section) = symbol.section { + if out_sections_index[section.0].is_none() { + continue; + } + } + + let name = if symbol.name.is_empty() { + None + } else { + Some(writer.add_string(symbol.name)) + }; + + out_syms.push(SymbolOut { + id: symbol.id, + name, + }); + } + } + let num_local = 1 + out_syms + .iter() + .take_while(|sym| self.symbols.0[sym.id.0].st_bind() == elf::STB_LOCAL) + .count() as u32; + let mut out_syms_index = vec![Default::default(); self.symbols.0.len()]; + for out_sym in out_syms.iter_mut() { + // FIXME: symtab_shndx handling + out_syms_index[out_sym.id.0] = Some(writer.reserve_symbol_index(None)); + } + + // Count the versions and add version strings. + // TODO: omit unused versions + let mut verdef_count = 0; + let mut verdaux_count = 0; + let mut verneed_count = 0; + let mut vernaux_count = 0; + let mut out_version_files = vec![VersionFileOut::default(); self.version_files.0.len()]; + if let Some(version_base) = self.version_base { + verdef_count += 1; + verdaux_count += 1; + writer.add_dynamic_string(version_base); + } + for version in &self.versions.0 { + match version { + Version::Def(def) => { + verdef_count += 1; + verdaux_count += def.names.len(); + for name in &def.names { + writer.add_dynamic_string(name); + } + } + Version::Need(need) => { + vernaux_count += 1; + writer.add_dynamic_string(need.name); + out_version_files[need.file.0].versions.push(need.id); + } + } + } + for file in &self.version_files.0 { + if out_version_files[file.id.0].versions.is_empty() { + continue; + } + verneed_count += 1; + writer.add_dynamic_string(file.name); + } + + // Build the GNU attributes section. + let mut gnu_attributes = Vec::new(); + if !self.gnu_attributes.subsections.is_empty() { + let mut writer = writer.attributes_writer(); + for subsection in &self.gnu_attributes.subsections { + writer.start_subsection(subsection.vendor); + for subsubsection in &subsection.subsubsections { + writer.start_subsubsection(subsubsection.tag.tag()); + match &subsubsection.tag { + AttributeTag::File => {} + AttributeTag::Section(sections) => { + for id in sections { + if let Some(index) = out_sections_index[id.0] { + writer.write_subsubsection_index(index.0); + } + } + writer.write_subsubsection_index(0); + } + AttributeTag::Symbol(symbols) => { + for id in symbols { + if let Some(index) = out_syms_index[id.0] { + writer.write_subsubsection_index(index.0); + } + } + writer.write_subsubsection_index(0); + } + } + writer.write_subsubsection_attributes(subsubsection.data); + writer.end_subsubsection(); + } + writer.end_subsection(); + } + gnu_attributes = writer.data(); + assert_ne!(gnu_attributes.len(), 0); + } + + // Start reserving file ranges. + writer.reserve_file_header(); + + let mut hash_addr = 0; + let mut gnu_hash_addr = 0; + let mut versym_addr = 0; + let mut verdef_addr = 0; + let mut verneed_addr = 0; + let mut dynamic_addr = 0; + let mut dynsym_addr = 0; + let mut dynstr_addr = 0; + + if !self.segments.0.is_empty() { + // TODO: support program headers in other locations. + if self.e_phoff != writer.reserved_len() { + return Err(Error(format!( + "Unsupported e_phoff value 0x{:x}", + self.e_phoff + ))); + } + let num_segments = self + .segments + .0 + .iter() + .filter(|segment| !segment.delete) + .count(); + writer.reserve_program_headers(num_segments as u32); + } + + let mut alloc_sections = Vec::new(); + if !self.segments.0.is_empty() { + // Reserve alloc sections at original offsets. + alloc_sections = self + .sections + .0 + .iter() + .filter(|section| section.sh_flags & u64::from(elf::SHF_ALLOC) != 0) + .collect(); + // The data for alloc sections may need to be written in a different order + // from their section headers. + alloc_sections.sort_by_key(|section| section.sh_offset); + for section in &alloc_sections { + if section.sh_offset < writer.reserved_len() { + return Err(Error(format!( + "Unsupported sh_offset value 0x{:x}, expected at least 0x{:x}", + section.sh_offset, + writer.reserved_len(), + ))); + } + // The input sh_offset needs to be preserved so that offsets in program + // headers are correct. + writer.reserve_until(section.sh_offset); + let offset = match §ion.data { + SectionData::Data(data) => writer.reserve(data.len(), section.sh_addralign), + SectionData::UninitializedData(_) => { + // Note: unaligned input sh_offset was observed in practice. + writer.reserve(0, 1) + } + SectionData::Relocation => writer.reserve_relocations( + section.relocations.len(), + section.sh_type == elf::SHT_RELA, + ), + SectionData::Dynamic => { + dynamic_addr = section.sh_addr; + writer.reserve_dynamic(self.dynamics.len()) + } + SectionData::DynamicSymbol => { + dynsym_addr = section.sh_addr; + writer.reserve_dynsym() + } + SectionData::DynamicString => { + dynstr_addr = section.sh_addr; + writer.reserve_dynstr() + } + SectionData::Hash => { + hash_addr = section.sh_addr; + writer.reserve_hash(self.hash_bucket_count, hash_chain_count) + } + SectionData::GnuHash => { + gnu_hash_addr = section.sh_addr; + writer.reserve_gnu_hash( + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + gnu_hash_symbol_count, + ) + } + SectionData::GnuVersym => { + versym_addr = section.sh_addr; + writer.reserve_gnu_versym() + } + SectionData::GnuVerdef => { + verdef_addr = section.sh_addr; + writer.reserve_gnu_verdef(verdef_count, verdaux_count) + } + SectionData::GnuVerneed => { + verneed_addr = section.sh_addr; + writer.reserve_gnu_verneed(verneed_count, vernaux_count) + } + _ => { + return Err(Error(format!( + "Unsupported alloc section type {:x}", + section.sh_type + ))); + } + }; + if section.sh_offset != offset { + return Err(Error(format!( + "Unaligned sh_offset value 0x{:x}", + section.sh_offset + ))); + } + out_sections[section.id.0].offset = offset; + } + } + + // Reserve non-alloc sections at any offset. + for section in &self.sections.0 { + if !self.segments.0.is_empty() && section.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + match §ion.data { + SectionData::Data(data) => { + out_sections[section.id.0].offset = + writer.reserve(data.len(), section.sh_addralign); + } + SectionData::UninitializedData(_) => { + out_sections[section.id.0].offset = writer.reserve(0, section.sh_addralign); + } + SectionData::GnuAttributes => { + writer.reserve_gnu_attributes(gnu_attributes.len()); + } + // FIXME + _ => {} + } + } + + writer.reserve_symtab(); + writer.reserve_symtab_shndx(); + writer.reserve_strtab(); + + // Reserve non-alloc relocations. + for section in &self.sections.0 { + if !self.segments.0.is_empty() && section.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + match section.data { + SectionData::Relocation => {} + _ => continue, + } + // FIXME: deleted symbols + out_sections[section.id.0].offset = writer + .reserve_relocations(section.relocations.len(), section.sh_type == elf::SHT_RELA); + } + + writer.reserve_shstrtab(); + writer.reserve_section_headers(); + + // Start writing. + writer.write_file_header(&write::elf::FileHeader { + os_abi: self.os_abi, + abi_version: self.abi_version, + e_type: self.e_type, + e_machine: self.e_machine, + e_entry: self.e_entry, + e_flags: self.e_flags, + })?; + + if !self.segments.0.is_empty() { + writer.write_align_program_headers(); + for segment in &self.segments.0 { + if segment.delete { + continue; + } + 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, + }); + } + } + + // Write alloc sections. + if !self.segments.0.is_empty() { + for section in alloc_sections { + writer.pad_until(section.sh_offset); + match §ion.data { + SectionData::Data(data) => { + debug_assert_eq!(out_sections[section.id.0].offset, writer.len()); + writer.write(data); + } + SectionData::UninitializedData(_) => {} + SectionData::Relocation => { + writer.write_align_relocation(); + for rel in §ion.relocations { + let r_sym = if let Some(symbol) = rel.symbol { + // FIXME: deleted symbol + out_dynsyms_index[symbol.0].unwrap().0 + } else { + 0 + }; + writer.write_relocation( + true, + &write::elf::Rel { + r_offset: rel.r_offset, + r_sym, + r_type: rel.r_type, + r_addend: rel.r_addend, + }, + ); + } + } + SectionData::Dynamic => { + for (d, out) in self.dynamics.iter().zip(out_dynamics.iter()) { + match *d { + Dynamic::Integer { tag, val } => { + // TODO: fix values? + writer.write_dynamic(tag, val); + } + Dynamic::String { tag, .. } => { + writer.write_dynamic_string(tag, out.string.unwrap()); + } + } + } + } + SectionData::DynamicSymbol => { + writer.write_null_dynamic_symbol(); + for out_sym in &out_dynsyms { + let symbol = &self.dynamic_symbols.0[out_sym.id.0]; + let section = + symbol.section.map(|id| out_sections_index[id.0].unwrap()); + writer.write_dynamic_symbol(&write::elf::Sym { + name: out_sym.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(); + } + SectionData::Hash => { + if self.hash_bucket_count == 0 { + return Err(Error(format!(".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(format!(".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, + |index| { + out_dynsyms[gnu_hash_index_base + index as usize] + .gnu_hash + .unwrap() + }, + ); + } + SectionData::GnuVersym => { + writer.write_null_gnu_versym(); + for out_dynsym in &out_dynsyms { + let symbol = &self.dynamic_symbols.0[out_dynsym.id.0]; + let mut index = symbol.version.0 as u16; + if symbol.version_hidden { + index |= elf::VERSYM_HIDDEN; + } + writer.write_gnu_versym(index); + } + } + SectionData::GnuVerdef => { + writer.write_align_gnu_verdef(); + if let Some(version_base) = self.version_base { + writer.write_gnu_verdef(&write::elf::Verdef { + version: elf::VER_DEF_CURRENT, + flags: elf::VER_FLG_BASE, + index: 1, + aux_count: 1, + name: writer.get_dynamic_string(version_base), + }); + } + for version in &self.versions { + if let Version::Def(def) = version { + let mut names = def.names.iter(); + let name = names.next().ok_or_else(|| { + Error(format!("Missing SHT_GNU_VERDEF name {}", def.id.0)) + })?; + writer.write_gnu_verdef(&write::elf::Verdef { + version: elf::VER_DEF_CURRENT, + flags: def.flags, + index: def.id.0 as u16, + aux_count: def.names.len() as u16, + name: writer.get_dynamic_string(name), + }); + for name in names { + writer.write_gnu_verdaux(writer.get_dynamic_string(name)); + } + } + } + } + SectionData::GnuVerneed => { + writer.write_align_gnu_verneed(); + for file in &self.version_files.0 { + let out_file = &out_version_files[file.id.0]; + if out_file.versions.is_empty() { + continue; + } + writer.write_gnu_verneed(&write::elf::Verneed { + version: elf::VER_NEED_CURRENT, + aux_count: out_file.versions.len() as u16, + file: writer.get_dynamic_string(file.name), + }); + for id in &out_file.versions { + // This will always match. + if let Version::Need(need) = &self.versions.0[id.0 - 2] { + debug_assert_eq!(*id, need.id); + writer.write_gnu_vernaux(&write::elf::Vernaux { + flags: need.flags, + index: need.id.0 as u16, + name: writer.get_dynamic_string(need.name), + }); + } + } + } + } + _ => { + return Err(Error(format!( + "Unsupported alloc section type {:x}", + section.sh_type + ))); + } + } + } + } + + // Write non-alloc sections. + for section in &self.sections.0 { + if !self.segments.0.is_empty() && section.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + match §ion.data { + SectionData::Data(data) => { + writer.write_align(section.sh_addralign); + debug_assert_eq!(out_sections[section.id.0].offset, writer.len()); + writer.write(data); + } + SectionData::GnuAttributes => { + writer.write_gnu_attributes(&gnu_attributes); + } + _ => {} + } + } + + writer.write_null_symbol(); + for out_sym in &out_syms { + let symbol = &self.symbols.0[out_sym.id.0]; + let section = symbol.section.map(|id| out_sections_index[id.0].unwrap()); + writer.write_symbol(&write::elf::Sym { + name: out_sym.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, + }); + } + writer.write_symtab_shndx(); + writer.write_strtab(); + + // Write non-alloc relocations. + for section in &self.sections.0 { + if !self.segments.0.is_empty() && section.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + match section.data { + SectionData::Relocation => {} + _ => continue, + } + let out_syms = if section.sh_link_section.is_none() { + // FIXME: error + continue; + } else if section.sh_link_section == symtab_id { + &out_syms_index + } else if section.sh_link_section == symtab_id { + &out_dynsyms_index + } else { + // FIXME: error + continue; + }; + writer.write_align_relocation(); + for rel in §ion.relocations { + let r_sym = if let Some(symbol) = rel.symbol { + // FIXME: deleted symbol + out_syms[symbol.0].unwrap().0 + } else { + 0 + }; + writer.write_relocation( + true, + &write::elf::Rel { + r_offset: rel.r_offset, + r_sym, + r_type: rel.r_type, + r_addend: rel.r_addend, + }, + ); + } + } + + writer.write_shstrtab(); + + writer.write_null_section_header(); + for out_section in &out_sections { + let section = &self.sections.0[out_section.id.0]; + match §ion.data { + SectionData::Data(_) + | SectionData::UninitializedData(_) + | SectionData::Relocation => { + let sh_size = match section.data { + SectionData::Data(ref data) => data.len() as u64, + SectionData::UninitializedData(len) => len, + SectionData::Relocation => writer.relocations_size( + section.relocations.len(), + section.sh_type == elf::SHT_RELA, + ) as u64, + _ => 0, + }; + let sh_link = if let Some(id) = section.sh_link_section { + // FIXME: error handling + out_sections_index[id.0].unwrap().0 + } else { + 0 + }; + let sh_info = if let Some(id) = section.sh_info_section { + // FIXME: error handling + out_sections_index[id.0].unwrap().0 + } else { + section.sh_info + }; + writer.write_section_header(&write::elf::SectionHeader { + name: out_section.name, + sh_type: section.sh_type, + sh_flags: section.sh_flags, + sh_addr: section.sh_addr, + sh_offset: out_section.offset as u64, + sh_size, + sh_link, + sh_info, + sh_addralign: section.sh_addralign as u64, + sh_entsize: section.sh_entsize, + }); + } + SectionData::SectionString => { + writer.write_shstrtab_section_header(); + } + SectionData::Symbol => { + writer.write_symtab_section_header(num_local); + } + SectionData::SymbolSectionIndex => { + writer.write_symtab_shndx_section_header(); + } + SectionData::String => { + writer.write_strtab_section_header(); + } + SectionData::Dynamic => { + writer.write_dynamic_section_header(dynamic_addr); + } + SectionData::DynamicString => { + writer.write_dynstr_section_header(dynstr_addr); + } + SectionData::DynamicSymbol => { + writer.write_dynsym_section_header(dynsym_addr, 1); + } + SectionData::Hash => { + writer.write_hash_section_header(hash_addr); + } + SectionData::GnuHash => { + writer.write_gnu_hash_section_header(gnu_hash_addr); + } + SectionData::GnuVersym => { + writer.write_gnu_versym_section_header(versym_addr); + } + SectionData::GnuVerdef => { + writer.write_gnu_verdef_section_header(verdef_addr); + } + SectionData::GnuVerneed => { + writer.write_gnu_verneed_section_header(verneed_addr); + } + SectionData::GnuAttributes => { + writer.write_gnu_attributes_section_header(); + } + } + } + debug_assert_eq!(writer.reserved_len(), writer.len()); + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SegmentId(usize); + +#[derive(Debug)] +pub struct Segment<'data> { + id: SegmentId, + pub p_type: u32, + pub p_flags: u32, + pub p_offset: u64, + pub p_vaddr: u64, + pub p_paddr: u64, + pub p_filesz: u64, + pub p_memsz: u64, + pub p_align: u64, + + pub delete: bool, + + // Might need to reference data if no sections. + marker: PhantomData<&'data ()>, +} + +impl<'data> Segment<'data> { + pub fn id(&self) -> SegmentId { + self.id + } +} + +#[derive(Debug)] +pub struct Segments<'data>(Vec>); + +impl<'data> Segments<'data> { + fn new() -> Self { + Segments(Vec::new()) + } + + pub fn add(&mut self) -> &mut Segment<'data> { + let id = SegmentId(self.0.len()); + self.0.push(Segment { + id, + p_type: 0, + p_flags: 0, + p_offset: 0, + p_vaddr: 0, + p_paddr: 0, + p_filesz: 0, + p_memsz: 0, + p_align: 0, + delete: false, + marker: PhantomData, + }); + self.0.last_mut().unwrap() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SectionId(usize); + +#[derive(Debug)] +pub struct Section<'data> { + id: SectionId, + pub segment: Option, + pub name: &'data [u8], + pub sh_type: u32, + pub sh_flags: u64, + pub sh_addr: u64, + pub sh_offset: usize, + pub sh_link_section: Option, + pub sh_info: u32, + pub sh_info_section: Option, + pub sh_addralign: usize, + pub sh_entsize: u64, + + pub data: SectionData<'data>, + pub relocations: Vec, + pub delete: bool, +} + +impl<'data> Section<'data> { + pub fn id(&self) -> SectionId { + self.id + } +} + +#[derive(Debug)] +pub enum SectionData<'data> { + Data(&'data [u8]), + UninitializedData(u64), + Relocation, + SectionString, + Symbol, + SymbolSectionIndex, + String, + Dynamic, + DynamicSymbol, + DynamicString, + Hash, + GnuHash, + GnuVersym, + GnuVerdef, + GnuVerneed, + GnuAttributes, +} + +#[derive(Debug)] +pub struct Sections<'data>(Vec>); + +impl<'data> Sections<'data> { + fn new() -> Self { + Sections(Vec::new()) + } +} + +// include dynamic:bool? +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SymbolId(usize); + +#[derive(Debug)] +pub struct Symbol<'data> { + id: SymbolId, + pub name: &'data [u8], + pub section: Option, + pub st_info: u8, + pub st_other: u8, + pub st_shndx: u16, + pub st_value: u64, + pub st_size: u64, + pub delete: bool, + /// Version for dynamic symbols. + pub version: VersionId, + pub version_hidden: bool, +} + +impl<'data> Symbol<'data> { + /// Get the `st_bind` component of the `st_info` field. + #[inline] + pub fn st_bind(&self) -> u8 { + self.st_info >> 4 + } + + /// Get the `st_type` component of the `st_info` field. + #[inline] + pub fn st_type(&self) -> u8 { + self.st_info & 0xf + } + + /// Set the `st_info` field given the `st_bind` and `st_type` components. + #[inline] + pub fn set_st_info(&mut self, st_bind: u8, st_type: u8) { + self.st_info = (st_bind << 4) + (st_type & 0xf); + } +} + +#[derive(Debug)] +pub struct Symbols<'data>(Vec>); + +impl<'data> Symbols<'data> { + fn new() -> Self { + Symbols(Vec::new()) + } +} + +impl<'a, 'data> IntoIterator for &'a Symbols<'data> { + type Item = &'a Symbol<'data>; + type IntoIter = core::slice::Iter<'a, Symbol<'data>>; + fn into_iter(self) -> core::slice::Iter<'a, Symbol<'data>> { + self.0.as_slice().into_iter() + } +} + +impl<'a, 'data> IntoIterator for &'a mut Symbols<'data> { + type Item = &'a mut Symbol<'data>; + type IntoIter = core::slice::IterMut<'a, Symbol<'data>>; + fn into_iter(self) -> core::slice::IterMut<'a, Symbol<'data>> { + self.0.as_mut_slice().into_iter() + } +} + +#[derive(Debug)] +pub struct Relocation { + pub r_offset: u64, + pub symbol: Option, + pub r_type: u32, + pub r_addend: i64, + pub delete: bool, +} + +#[derive(Debug)] +pub enum Dynamic<'data> { + Integer { tag: u32, val: u64 }, + String { tag: u32, val: &'data [u8] }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VersionFileId(usize); + +#[derive(Debug)] +pub struct VersionFile<'data> { + id: VersionFileId, + pub name: &'data [u8], +} + +#[derive(Debug)] +pub struct VersionFiles<'data>(Vec>); + +impl<'data> VersionFiles<'data> { + fn new() -> Self { + VersionFiles(Vec::new()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VersionId(usize); + +impl VersionId { + pub fn none() -> Self { + VersionId(0) + } + + pub fn global() -> Self { + VersionId(1) + } +} + +#[derive(Debug)] +pub enum Version<'data> { + Def(VersionDef<'data>), + Need(VersionNeed<'data>), +} + +impl<'data> Version<'data> { + pub fn id(&self) -> VersionId { + match self { + Version::Def(def) => def.id(), + Version::Need(def) => def.id(), + } + } +} + +#[derive(Debug)] +pub struct VersionDef<'data> { + id: VersionId, + pub flags: u16, + pub names: Vec<&'data [u8]>, +} + +impl<'data> VersionDef<'data> { + pub fn id(&self) -> VersionId { + self.id + } +} + +#[derive(Debug)] +pub struct VersionNeed<'data> { + id: VersionId, + pub flags: u16, + pub name: &'data [u8], + pub file: VersionFileId, +} + +impl<'data> VersionNeed<'data> { + pub fn id(&self) -> VersionId { + self.id + } +} + +#[derive(Debug)] +pub struct Versions<'data>(Vec>); + +impl<'data> Versions<'data> { + fn new() -> Self { + Versions(Vec::new()) + } +} + +impl<'a, 'data> IntoIterator for &'a Versions<'data> { + type Item = &'a Version<'data>; + type IntoIter = core::slice::Iter<'a, Version<'data>>; + fn into_iter(self) -> core::slice::Iter<'a, Version<'data>> { + self.0.as_slice().into_iter() + } +} + +impl<'a, 'data> IntoIterator for &'a mut Versions<'data> { + type Item = &'a mut Version<'data>; + type IntoIter = core::slice::IterMut<'a, Version<'data>>; + fn into_iter(self) -> core::slice::IterMut<'a, Version<'data>> { + self.0.as_mut_slice().into_iter() + } +} + +#[derive(Debug)] +pub struct AttributesSection<'data> { + pub subsections: Vec>, +} + +impl<'data> AttributesSection<'data> { + pub fn new() -> Self { + AttributesSection { + subsections: Vec::new(), + } + } +} + +#[derive(Debug)] +pub struct AttributesSubsection<'data> { + pub vendor: &'data [u8], + pub subsubsections: Vec>, +} + +impl<'data> AttributesSubsection<'data> { + pub fn new(vendor: &'data [u8]) -> Self { + AttributesSubsection { + vendor, + subsubsections: Vec::new(), + } + } +} + +#[derive(Debug)] +pub struct AttributesSubsubsection<'data> { + pub tag: AttributeTag, + pub data: &'data [u8], +} + +#[derive(Debug)] +pub enum AttributeTag { + File, + Section(Vec), + Symbol(Vec), +} + +impl AttributeTag { + pub fn tag(&self) -> u8 { + match self { + AttributeTag::File => elf::Tag_File, + AttributeTag::Section(_) => elf::Tag_Section, + AttributeTag::Symbol(_) => elf::Tag_Symbol, + } + } +} diff --git a/src/write/elf/mod.rs b/src/write/elf/mod.rs index 3a4f3716..d3341a23 100644 --- a/src/write/elf/mod.rs +++ b/src/write/elf/mod.rs @@ -7,3 +7,6 @@ mod object; mod writer; pub use writer::*; + +mod builder; +pub use builder::*; diff --git a/src/write/elf/writer.rs b/src/write/elf/writer.rs index 97509249..b2b4dfb2 100644 --- a/src/write/elf/writer.rs +++ b/src/write/elf/writer.rs @@ -991,15 +991,16 @@ impl<'a> Writer<'a> { /// /// This function does nothing if no dynamic strings or symbols were defined. /// This must be called after [`Self::add_dynamic_string`]. - pub fn reserve_dynstr(&mut self) { + pub fn reserve_dynstr(&mut self) -> usize { debug_assert_eq!(self.dynstr_offset, 0); if !self.need_dynstr { - return; + return 0; } // Start with null string. self.dynstr_data = vec![0]; self.dynstr.write(1, &mut self.dynstr_data); self.dynstr_offset = self.reserve(self.dynstr_data.len(), 1); + self.dynstr_offset } /// Write the dynamic string table. @@ -1100,15 +1101,16 @@ impl<'a> Writer<'a> { /// /// This function does nothing if no dynamic symbols were reserved. /// This must be called after [`Self::reserve_dynamic_symbol_index`]. - pub fn reserve_dynsym(&mut self) { + pub fn reserve_dynsym(&mut self) -> usize { debug_assert_eq!(self.dynsym_offset, 0); if self.dynsym_num == 0 { - return; + return 0; } self.dynsym_offset = self.reserve( self.dynsym_num as usize * self.symbol_size(), self.elf_align, ); + self.dynsym_offset } /// Write the null dynamic symbol. @@ -1219,13 +1221,14 @@ impl<'a> Writer<'a> { /// Reserve the range for the `.dynamic` section. /// /// This function does nothing if `dynamic_num` is zero. - pub fn reserve_dynamic(&mut self, dynamic_num: usize) { + pub fn reserve_dynamic(&mut self, dynamic_num: usize) -> usize { debug_assert_eq!(self.dynamic_offset, 0); if dynamic_num == 0 { - return; + return 0; } self.dynamic_num = dynamic_num; self.dynamic_offset = self.reserve(dynamic_num * self.dyn_size(), self.elf_align); + self.dynamic_offset } /// Write alignment padding bytes prior to the `.dynamic` section. @@ -1314,11 +1317,12 @@ impl<'a> Writer<'a> { /// /// `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) { + pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) -> usize { self.hash_size = mem::size_of::>() + bucket_count as usize * 4 + chain_count as usize * 4; self.hash_offset = self.reserve(self.hash_size, self.elf_align); + self.hash_offset } /// Write a SysV hash section. @@ -1381,12 +1385,18 @@ impl<'a> Writer<'a> { /// /// `symbol_count` is the number of symbols in the hash, /// not the total number of symbols. - pub fn reserve_gnu_hash(&mut self, bloom_count: u32, bucket_count: u32, symbol_count: u32) { + pub fn reserve_gnu_hash( + &mut self, + bloom_count: u32, + bucket_count: u32, + symbol_count: u32, + ) -> usize { self.gnu_hash_size = mem::size_of::>() + bloom_count as usize * self.elf_align + bucket_count as usize * 4 + symbol_count as usize * 4; self.gnu_hash_offset = self.reserve(self.gnu_hash_size, self.elf_align); + self.gnu_hash_offset } /// Write a GNU hash section. @@ -1501,12 +1511,13 @@ impl<'a> Writer<'a> { /// Reserve the range for the `.gnu.version` section. /// /// This function does nothing if no dynamic symbols were reserved. - pub fn reserve_gnu_versym(&mut self) { + pub fn reserve_gnu_versym(&mut self) -> usize { debug_assert_eq!(self.gnu_versym_offset, 0); if self.dynsym_num == 0 { - return; + return 0; } self.gnu_versym_offset = self.reserve(self.dynsym_num as usize * 2, 2); + self.gnu_versym_offset } /// Write the null symbol version entry. @@ -1556,16 +1567,17 @@ impl<'a> Writer<'a> { } /// Reserve the range for the `.gnu.version_d` section. - pub fn reserve_gnu_verdef(&mut self, verdef_count: usize, verdaux_count: usize) { + pub fn reserve_gnu_verdef(&mut self, verdef_count: usize, verdaux_count: usize) -> usize { debug_assert_eq!(self.gnu_verdef_offset, 0); if verdef_count == 0 { - return; + return 0; } self.gnu_verdef_size = verdef_count * mem::size_of::>() + verdaux_count * mem::size_of::>(); self.gnu_verdef_offset = self.reserve(self.gnu_verdef_size, self.elf_align); self.gnu_verdef_count = verdef_count as u16; self.gnu_verdef_remaining = self.gnu_verdef_count; + self.gnu_verdef_offset } /// Write alignment padding bytes prior to a `.gnu.version_d` section. @@ -1651,16 +1663,17 @@ impl<'a> Writer<'a> { } /// Reserve the range for the `.gnu.version_r` section. - pub fn reserve_gnu_verneed(&mut self, verneed_count: usize, vernaux_count: usize) { + pub fn reserve_gnu_verneed(&mut self, verneed_count: usize, vernaux_count: usize) -> usize { debug_assert_eq!(self.gnu_verneed_offset, 0); if verneed_count == 0 { - return; + return 0; } self.gnu_verneed_size = verneed_count * mem::size_of::>() + vernaux_count * mem::size_of::>(); self.gnu_verneed_offset = self.reserve(self.gnu_verneed_size, self.elf_align); self.gnu_verneed_count = verneed_count as u16; self.gnu_verneed_remaining = self.gnu_verneed_count; + self.gnu_verneed_offset } /// Write alignment padding bytes prior to a `.gnu.version_r` section. @@ -1793,11 +1806,16 @@ impl<'a> Writer<'a> { self.buffer.write_bytes(data); } + /// Return the file size for the given number of relocations. + pub fn relocations_size(&self, count: usize, is_rela: bool) -> usize { + count * self.rel_size(is_rela) + } + /// Reserve a file range for the given number of relocations. /// /// Returns the offset of the range. pub fn reserve_relocations(&mut self, count: usize, is_rela: bool) -> usize { - self.reserve(count * self.rel_size(is_rela), self.elf_align) + self.reserve(self.relocations_size(count, is_rela), self.elf_align) } /// Write alignment padding bytes prior to a relocation section.