diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 40d7fac2..84af804c 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -39,8 +39,9 @@ jobs: submodules: true - run: cargo test --no-default-features --features read - run: cargo test --no-default-features --features write + - run: cargo test --no-default-features --features copy - run: cargo test --no-default-features --features read_core,write_core,coff - - run: cargo test --no-default-features --features read_core,write_core,elf + - run: cargo test --no-default-features --features copy_core,elf - run: cargo test --no-default-features --features read_core,write_core,macho - run: cargo test --no-default-features --features read_core,write_core,pe - run: cargo test --no-default-features --features read_core,wasm diff --git a/Cargo.toml b/Cargo.toml index 55f08974..6ea41c62 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,10 @@ write_core = ["dep:crc32fast", "dep:indexmap", "dep:hashbrown"] write_std = ["write_core", "std", "indexmap?/std", "crc32fast?/std"] # Write support for all file formats, including libstd features. write = ["write_std", "coff", "elf", "macho", "pe", "xcoff"] +# Core copy support. You will need to enable some file formats too. +copy_core = ["read_core", "write_core"] +# Copy support for all file formats. +copy = ["copy_core", "std", "elf"] #======================================= # Misc features. @@ -75,7 +79,7 @@ default = ["read", "compression"] #======================================= # Umbrella feature for enabling all user-facing features of this crate. Does not # enable internal features like `rustc-dep-of-std`. -all = ["read", "write", "std", "compression", "wasm"] +all = ["read", "write", "copy", "std", "compression", "wasm"] # Use of --all-features is not supported. # This is a dummy feature to detect when --all-features is used. @@ -84,7 +88,7 @@ cargo-all = [] #======================================= # Documentation should be generated with everything in "all" except for "unaligned". doc = [ - "read_core", "write_std", + "read_core", "write_std", "copy_core", "std", "compression", "archive", "coff", "elf", "macho", "pe", "wasm", "xcoff", ] diff --git a/crates/examples/Cargo.toml b/crates/examples/Cargo.toml index b2ae2cc4..ab61714f 100644 --- a/crates/examples/Cargo.toml +++ b/crates/examples/Cargo.toml @@ -4,6 +4,8 @@ version = "0.0.0" edition = "2018" [dependencies] +anyhow = "1.0.79" +bumpalo = "3.14.0" clap = "4.3.24" memmap2 = "0.7.1" object = { path = "../..", default-features = false } @@ -14,9 +16,10 @@ glob = "0.3" [features] read = ["object/read"] write = ["object/write"] +copy = ["object/copy"] wasm = ["object/wasm"] xcoff = ["object/xcoff"] -all = ["read", "write", "wasm", "xcoff"] +all = ["read", "write", "copy", "wasm", "xcoff"] unstable-all = ["all"] default = ["read"] @@ -30,7 +33,7 @@ required-features = ["object/read"] [[bin]] name = "elfcopy" -required-features = ["object/read_core", "object/write_core", "object/elf", "object/std"] +required-features = ["copy"] [[bin]] name = "elftoefi" diff --git a/crates/examples/src/bin/elfcopy.rs b/crates/examples/src/bin/elfcopy.rs index c2518f81..aaaf3f0d 100644 --- a/crates/examples/src/bin/elfcopy.rs +++ b/crates/examples/src/bin/elfcopy.rs @@ -1,1003 +1,139 @@ -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::fs::File; +use std::fs; use std::io::{self, BufRead}; - -fn main() { - let mut args = env::args(); - if !(args.len() == 3 || args.len() == 5) { - eprintln!( - "Usage: {} [--redefine-syms ] ", - args.next().unwrap() - ); - process::exit(1); - } - - args.next(); - - let redefine_file = match args.len() { - // 4 tokens remaining means we have specified --redefine-syms - 4 => { - if args.next() != Some("--redefine-syms".to_string()) { - eprintln!("Usage: [--redefine-syms ] "); - process::exit(1); - } - Some(args.next().unwrap()) - } - _ => None, - }; - - let in_file_path = args.next().unwrap(); - let out_file_path = args.next().unwrap(); - - let in_file = match fs::File::open(&in_file_path) { - Ok(file) => file, - Err(err) => { - eprintln!("Failed to open file '{}': {}", in_file_path, err,); - process::exit(1); - } - }; - let in_data = match unsafe { memmap2::Mmap::map(&in_file) } { - Ok(mmap) => mmap, - Err(err) => { - eprintln!("Failed to map file '{}': {}", in_file_path, err,); - process::exit(1); - } - }; +use std::path::PathBuf; + +use anyhow::{anyhow, Context, Result}; +use bumpalo::Bump; +use clap::{Arg, ArgAction, Command}; +use object_examples::rewrite; + +fn main() -> Result<()> { + let matches = Command::new("elfcopy") + .about("Copy and modify ELF files") + .max_term_width(100) + .args(&[ + Arg::new("in-file") + .required(true) + .value_parser(clap::value_parser!(PathBuf)), + Arg::new("out-file") + .required(true) + .value_parser(clap::value_parser!(PathBuf)), + Arg::new("redefine-sym") + .long("redefine-sym") + .value_name("old=new") + .value_parser(clap::value_parser!(String)) + .action(ArgAction::Append) + .help("Change the name of a symbol from to "), + Arg::new("redefine-syms") + .long("redefine-syms") + .value_name("file") + .value_parser(clap::value_parser!(PathBuf)) + .action(ArgAction::Append) + .help( + "Read a list of symbol names from and apply --redefine-sym for each. \ + Each line contains two symbols separated by whitespace.", + ), + Arg::new("remove-section") + .long("remove-section") + .value_name("section") + .value_parser(clap::value_parser!(String)) + .action(ArgAction::Append) + .help("Remove
"), + Arg::new("rename-section") + .long("rename-section") + .value_name("old=new") + .value_parser(clap::value_parser!(String)) + .action(ArgAction::Append) + .help("Change the name of a section from to "), + ]) + .get_matches(); + + let bump = Bump::new(); + let mut options = rewrite::Options::default(); + + for arg in matches + .get_many::("redefine-sym") + .unwrap_or_default() + { + let names: Vec<&str> = arg.split('=').take(2).collect(); + if names.len() != 2 { + return Err( + anyhow!("Invalid redefine sym: `{}`. --redefine-sym expects argument of the form: =", arg) + ); + } + options + .rename_symbol + .insert(names[0].as_bytes(), names[1].as_bytes()); + } + for filename in matches + .get_many::("redefine-syms") + .unwrap_or_default() + { + let file = fs::File::open(filename).with_context(|| { + format!("Failed to open redefine sym file '{}'", filename.display()) + })?; + for res in io::BufReader::new(file).lines() { + let line = res.with_context(|| { + format!("Failed to read redefine sym file '{}'", filename.display()) + })?; + let names: Vec<&str> = line.split(' ').take(2).collect(); + if names.len() != 2 { + return Err( + anyhow!( + "Invalid redefine file: `{}`. --redefine-syms expects lines of the form: ", line) + ); + } + options.rename_symbol.insert( + bump.alloc_slice_copy(names[0].as_bytes()), + bump.alloc_slice_copy(names[1].as_bytes()), + ); + } + } + for arg in matches + .get_many::("remove-section") + .unwrap_or_default() + { + options.delete_section.insert(arg.as_bytes()); + } + for arg in matches + .get_many::("rename-section") + .unwrap_or_default() + { + let names: Vec<&str> = arg.split('=').take(2).collect(); + if names.len() != 2 { + return Err( + anyhow!( + "Invalid rename section: `{}`. --rename-section expects argument of the form: =", arg) + ); + } + options + .rename_section + .insert(names[0].as_bytes(), names[1].as_bytes()); + } + + let in_file_path = matches.get_one::("in-file").unwrap(); + let out_file_path = matches.get_one::("out-file").unwrap(); + + let in_file = fs::File::open(in_file_path) + .with_context(|| format!("Failed to open input file '{}'", in_file_path.display()))?; + let in_data = unsafe { memmap2::Mmap::map(&in_file) } + .with_context(|| format!("Failed to map input file '{}'", in_file_path.display()))?; 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); - } - }; - 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: -/// ``` -/// -/// ``` -/// A symbol name can then be passed to query the corresponding new -/// name that we should provide the `out_*` variables in `copy_file`. -struct RedefineSymTable { - map: Option, Vec>>, -} - -impl RedefineSymTable { - fn new(filename: Option) -> Result> { - match filename { - Some(filename) => { - let file = File::open(filename)?; - - let mut map = HashMap::new(); - - for res in io::BufReader::new(file).lines() { - let line = res?; - let names: Vec<&str> = line.split(' ').take(2).collect(); - - // check that there are two symbol names on each line - if names.len() != 2 { - return Err( - "Error: invalid redefine file. --redefine-syms expects lines \ - of the form: " - .into(), - ); - } - - map.insert(names[0].into(), names[1].into()); - } - - Ok(Self { map: Some(map) }) - } - None => Ok(Self { map: None }), - } - } - - fn get_redefined_name<'a>(&'a self, original: &'a [u8]) -> &'a [u8] { - // check if we have a rename for this symbol - if let Some(map) = self.map.as_ref() { - if let Some(new_string) = map.get(original) { - return new_string.as_slice(); - } - } - - 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) + let mut builder = object::copy::elf::Builder::read(in_data) + .with_context(|| format!("Failed to parse input file '{}'", in_file_path.display()))?; + + rewrite::rewrite(&mut builder, &options)?; + + // FIXME: don't leave an empty file on failure + let out_file = fs::File::create(out_file_path) + .with_context(|| format!("Failed to create output file '{}'", out_file_path.display()))?; + let mut buffer = object::write::StreamingBuffer::new(out_file); + builder + .write(&mut buffer) + .context("Failed to generate output file")?; + buffer + .result() + .with_context(|| format!("Failed to write output file '{}'", out_file_path.display()))?; + Ok(()) } diff --git a/crates/examples/src/lib.rs b/crates/examples/src/lib.rs index c17c6e36..b5225d91 100644 --- a/crates/examples/src/lib.rs +++ b/crates/examples/src/lib.rs @@ -9,3 +9,6 @@ pub mod objdump; #[cfg(feature = "read")] pub mod readobj; + +#[cfg(feature = "copy")] +pub mod rewrite; diff --git a/crates/examples/src/rewrite.rs b/crates/examples/src/rewrite.rs new file mode 100644 index 00000000..33653a84 --- /dev/null +++ b/crates/examples/src/rewrite.rs @@ -0,0 +1,400 @@ +use std::collections::{HashMap, HashSet}; + +#[derive(Debug, Default)] +pub struct Options<'a> { + pub delete_symbol: HashSet<&'a [u8]>, + pub rename_symbol: HashMap<&'a [u8], &'a [u8]>, + pub delete_section: HashSet<&'a [u8]>, + pub rename_section: HashMap<&'a [u8], &'a [u8]>, +} + +pub fn rewrite<'data>( + builder: &mut object::copy::elf::Builder<'data>, + options: &Options<'data>, +) -> object::copy::Result<()> { + if !options.delete_symbol.is_empty() { + for symbol in &mut builder.dynamic_symbols { + if options.delete_symbol.contains(&symbol.name) { + symbol.delete = true; + } + } + for symbol in &mut builder.symbols { + if options.delete_symbol.contains(&symbol.name) { + symbol.delete = true; + } + } + } + if !options.rename_symbol.is_empty() { + for symbol in &mut builder.dynamic_symbols { + if let Some(name) = options.rename_symbol.get(symbol.name) { + symbol.name = name; + } + } + for symbol in &mut builder.symbols { + if let Some(name) = options.rename_symbol.get(symbol.name) { + symbol.name = name; + } + } + } + + if !options.delete_section.is_empty() { + for section in &mut builder.sections { + if options.delete_section.contains(§ion.name) { + section.delete = true; + // TODO: delete associated program header if present + } + } + } + if !options.rename_section.is_empty() { + for section in &mut builder.sections { + if let Some(name) = options.rename_section.get(section.name) { + section.name = name; + } + } + } + + move_sections(builder); + Ok(()) +} + +enum BlockKind { + FileHeader, + ProgramHeaders, + Section(object::copy::elf::SectionId), +} + +struct Block { + kind: BlockKind, + load_segment: object::copy::elf::SegmentId, + address: u64, + size: u64, + movable: bool, +} + +/// Move sections between segments if needed, and assign file offsets to segments and sections. +fn move_sections(builder: &mut object::copy::elf::Builder) { + builder.delete_orphan_symbols(); + builder.delete_orphan_relocations(); + builder.delete_unused_versions(); + builder.set_section_sizes(); + + // Calculate initial guess for the number of additional segments needed. + let mut added_p_flags = Vec::new(); + for section in &builder.sections { + // We consider sections that need an address but haven't been assigned one yet. + if section.delete || !section.is_alloc() || section.sh_offset != 0 { + continue; + } + // We need one PT_LOAD segment for each unique combination of p_flags. + let p_flags = section.p_flags(); + if !added_p_flags.contains(&p_flags) { + added_p_flags.push(p_flags); + } + } + let mut added_segments = added_p_flags.len(); + + // Loop until we reach a fixed point for the number of additional segments needed. + loop { + let move_sections = find_move_sections(builder, added_segments); + if move_sections.is_empty() { + return; + } + + // If moving a section that is part of a non-PT_LOAD segment, then we may need to + // split the segment, which will require an additional segment. + let mut split_segments = 0; + for segment in &mut builder.segments { + if segment.delete || segment.p_type == object::elf::PT_LOAD { + continue; + } + let mut any = false; + let mut all = true; + for id in &segment.sections { + if move_sections.contains(id) { + any = true; + } else { + all = false; + } + } + if !any || all { + continue; + } + split_segments += 1; + } + + // Recalculate the number of additional PT_LOAD segments needed. + added_p_flags.clear(); + for id in &move_sections { + let section = builder.sections.get_mut(*id); + section.sh_offset = 0; + let p_flags = section.p_flags(); + if !added_p_flags.contains(&p_flags) { + added_p_flags.push(p_flags); + } + } + + // Check if we have reached a fixed point for the number of additional segments needed. + if added_segments < split_segments + added_p_flags.len() { + added_segments = split_segments + added_p_flags.len(); + continue; + } + + println!( + "moving {} sections, adding {} segments", + move_sections.len(), + added_p_flags.len() + ); + + // Add the segments and assign sections to them. + for p_flags in added_p_flags { + // TODO: reuse segments that only contain movable sections + let segment = builder + .segments + .add_load_segment(p_flags, builder.load_align); + for id in &move_sections { + let section = builder.sections.get_mut(*id); + if p_flags == section.p_flags() { + segment.append_section(section); + println!( + "moved {} to {:x}, {:x}", + String::from_utf8_lossy(section.name), + section.sh_offset, + section.sh_addr + ); + } + } + } + + // Split or move non-PT_LOAD segments that contain sections that have been moved. + let sections = &builder.sections; + let mut split_segments = Vec::new(); + for segment in &mut builder.segments { + if segment.delete || segment.p_type == object::elf::PT_LOAD { + continue; + } + let mut any = false; + let mut all = true; + for id in &segment.sections { + if move_sections.contains(id) { + any = true; + } else { + all = false; + } + } + if !any { + continue; + } + if !all { + // Segment needs splitting. + // Remove all the sections that have been moved, and store them so + // that we can add the new segment later. + let mut split_sections = Vec::new(); + segment.sections.retain(|id| { + if move_sections.contains(id) { + split_sections.push(*id); + false + } else { + true + } + }); + split_segments.push((segment.id(), split_sections)); + } + + // The remaining sections have already been assigned an address. + // Shrink the segment to fit the remaining sections. + // TODO: verify that the sections are contiguous. If not, try to slide the sections + // down in memory. + segment.p_offset = segment + .sections + .iter() + .map(|id| sections.get(*id).sh_offset) + .min() + .unwrap(); + segment.p_vaddr = segment + .sections + .iter() + .map(|id| sections.get(*id).sh_addr) + .min() + .unwrap(); + segment.p_paddr = segment.p_vaddr; + segment.p_filesz = segment + .sections + .iter() + .map(|id| { + let section = sections.get(*id); + section.sh_offset + section.sh_size + }) + .max() + .unwrap() + - segment.p_offset; + segment.p_memsz = segment + .sections + .iter() + .map(|id| { + let section = sections.get(*id); + section.sh_addr + section.sh_size + }) + .max() + .unwrap() + - segment.p_vaddr; + } + // Add new segments due to splitting. + for (segment_id, split_sections) in split_segments { + let segment = builder.segments.copy(segment_id); + for id in split_sections { + let section = builder.sections.get_mut(id); + segment.append_section(section); + } + } + + return; + } +} + +fn find_move_sections( + builder: &object::copy::elf::Builder, + added_segments: usize, +) -> Vec { + use object::copy::elf::SectionData; + + let mut move_sections = Vec::new(); + 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()) + as u64; + + if let Some(segment) = builder.segments.find_load_segment_from_offset(0) { + let address = segment.address_from_offset(0); + blocks.push(Block { + kind: BlockKind::FileHeader, + load_segment: segment.id(), + address, + size: file_header_size, + movable: false, + }); + } + if let Some(segment) = builder + .segments + .find_load_segment_from_offset(builder.e_phoff) + { + let address = segment.address_from_offset(file_header_size); + blocks.push(Block { + kind: BlockKind::ProgramHeaders, + load_segment: segment.id(), + address, + size: program_headers_size, + movable: false, + }); + } + for section in &builder.sections { + if section.delete || !section.is_alloc() { + continue; + } + if section.sh_offset == 0 { + // Newly added section needs to be assigned to a segment. + move_sections.push(section.id()); + continue; + } + if section.sh_flags & u64::from(object::elf::SHF_TLS) != 0 { + // FIXME: I don't understand these + continue; + } + let Some(segment) = builder + .segments + .find_load_segment_from_offset(builder.e_phoff) + else { + // FIXME + panic!(); + }; + let movable = match §ion.data { + // Can't move sections whose address may referenced from + // a section that we can't rewrite. + SectionData::Data(_) | SectionData::UninitializedData(_) | SectionData::Dynamic(_) => { + false + } + // None of these can be referenced by address that I am aware of. + SectionData::Relocation(_) + | SectionData::Note(_) + | SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String + | SectionData::DynamicSymbol + | SectionData::DynamicString + | SectionData::Hash + | SectionData::GnuHash + | SectionData::GnuVersym + | SectionData::GnuVerdef + | SectionData::GnuVerneed + | SectionData::GnuAttributes => true, + }; + blocks.push(Block { + kind: BlockKind::Section(section.id()), + address: section.sh_addr, + size: section.sh_size, + load_segment: segment.id(), + movable, + }); + } + blocks.sort_by_key(|block| block.address); + + // For each block + // - if it is movable and overlaps the next block, then move the block. + // - if it is not movable and overlaps the next block, then try to move the next block. + // FIXME: better handling of overlap between segments, don't change segment address/size? + let mut i = 0; + while i + 1 < blocks.len() { + let end_address = blocks[i].address + blocks[i].size; + /* FIXME: looks like this isn't needed? + if blocks[i].load_segment != blocks[i+1].load_segment { + end_address = (end_address + (builder.load_align - 1)) & !(builder.load_align - 1); + } + */ + if end_address <= blocks[i + 1].address { + i += 1; + continue; + } + if blocks[i].movable { + if let BlockKind::Section(section) = blocks[i].kind { + println!( + "need to move {}, end_address {:x}", + String::from_utf8_lossy(builder.sections.get(section).name), + end_address + ); + move_sections.push(section); + blocks.remove(i); + } else { + // FIXME + panic!(); + } + } else if blocks[i + 1].movable { + if let BlockKind::Section(section) = blocks[i + 1].kind { + println!( + "need to move {}, end_address {:x}", + String::from_utf8_lossy(builder.sections.get(section).name), + end_address + ); + move_sections.push(section); + blocks.remove(i + 1); + } else { + // FIXME + panic!(); + } + } else { + // TODO: special handling for dynamic sections? + // FIXME + println!( + "address {}: {:x}-{:x}, {}: {:x}", + i, + blocks[i].address, + end_address, + i + 1, + blocks[i + 1].address + ); + panic!(); + } + } + move_sections.sort_by_key(|id| builder.sections.get(*id).sh_addr); + move_sections +} diff --git a/crates/examples/testfiles/rewrite/elf/base-noop b/crates/examples/testfiles/rewrite/elf/base-noop new file mode 100644 index 00000000..f7534dae --- /dev/null +++ b/crates/examples/testfiles/rewrite/elf/base-noop @@ -0,0 +1,1625 @@ +Format: ELF 64-bit +FileHeader { + Ident { + Magic: [7F, 45, 4C, 46] + Class: ELFCLASS64 (0x2) + Data: ELFDATA2LSB (0x1) + Version: EV_CURRENT (0x1) + OsAbi: ELFOSABI_SYSV (0x0) + AbiVersion: 0x0 + Unused: [0, 0, 0, 0, 0, 0, 0] + } + Type: ET_DYN (0x3) + Machine: EM_X86_64 (0x3E) + Version: EV_CURRENT (0x1) + Type: ET_DYN (0x3) + Entry: 0x570 + ProgramHeaderOffset: 0x40 + SectionHeaderOffset: 0x1948 + Flags: 0x0 + HeaderSize: 0x40 + ProgramHeaderEntrySize: 0x38 + ProgramHeaderCount: 9 + SectionHeaderEntrySize: 0x40 + SectionHeaderCount: 30 + SectionHeaderStringTableIndex: 29 +} +ProgramHeader { + Type: PT_PHDR (0x6) + Offset: 0x40 + VirtualAddress: 0x40 + PhysicalAddress: 0x40 + FileSize: 0x1F8 + MemorySize: 0x1F8 + Flags: 0x4 + PF_R (0x4) + Align: 0x8 +} +ProgramHeader { + Type: PT_INTERP (0x3) + Offset: 0x238 + VirtualAddress: 0x238 + PhysicalAddress: 0x238 + FileSize: 0x1C + MemorySize: 0x1C + Flags: 0x4 + PF_R (0x4) + Align: 0x1 +} +ProgramHeader { + Type: PT_LOAD (0x1) + Offset: 0x0 + VirtualAddress: 0x0 + PhysicalAddress: 0x0 + FileSize: 0x878 + MemorySize: 0x878 + Flags: 0x5 + PF_X (0x1) + PF_R (0x4) + Align: 0x200000 +} +ProgramHeader { + Type: PT_LOAD (0x1) + Offset: 0xDA8 + VirtualAddress: 0x200DA8 + PhysicalAddress: 0x200DA8 + FileSize: 0x268 + MemorySize: 0x270 + Flags: 0x6 + PF_W (0x2) + PF_R (0x4) + Align: 0x200000 +} +ProgramHeader { + Type: PT_DYNAMIC (0x2) + Offset: 0xDB8 + VirtualAddress: 0x200DB8 + PhysicalAddress: 0x200DB8 + FileSize: 0x200 + MemorySize: 0x200 + Flags: 0x6 + PF_W (0x2) + PF_R (0x4) + Align: 0x8 + Dynamic { + Tag: DT_NEEDED (0x1) + Value: "libc.so.6" + } + Dynamic { + Tag: DT_INIT (0xC) + Value: 0x520 + } + Dynamic { + Tag: DT_FINI (0xD) + Value: 0x714 + } + Dynamic { + Tag: DT_INIT_ARRAY (0x19) + Value: 0x200DA8 + } + Dynamic { + Tag: DT_INIT_ARRAYSZ (0x1B) + Value: 0x8 + } + Dynamic { + Tag: DT_FINI_ARRAY (0x1A) + Value: 0x200DB0 + } + Dynamic { + Tag: DT_FINI_ARRAYSZ (0x1C) + Value: 0x8 + } + Dynamic { + Tag: DT_HASH (0x4) + Value: 0x298 + } + Dynamic { + Tag: DT_GNU_HASH (0x6FFFFEF5) + Value: 0x2C8 + } + Dynamic { + Tag: DT_STRTAB (0x5) + Value: 0x390 + } + Dynamic { + Tag: DT_SYMTAB (0x6) + Value: 0x2E8 + } + Dynamic { + Tag: DT_STRSZ (0xA) + Value: 0x84 + } + Dynamic { + Tag: DT_SYMENT (0xB) + Value: 0x18 + } + Dynamic { + Tag: DT_DEBUG (0x15) + Value: 0x0 + } + Dynamic { + Tag: DT_PLTGOT (0x3) + Value: 0x200FB8 + } + Dynamic { + Tag: DT_PLTRELSZ (0x2) + Value: 0x18 + } + Dynamic { + Tag: DT_PLTREL (0x14) + Value: 0x7 + } + Dynamic { + Tag: DT_JMPREL (0x17) + Value: 0x508 + } + Dynamic { + Tag: DT_RELA (0x7) + Value: 0x448 + } + Dynamic { + Tag: DT_RELASZ (0x8) + Value: 0xC0 + } + Dynamic { + Tag: DT_RELAENT (0x9) + Value: 0x18 + } + Dynamic { + Tag: DT_FLAGS (0x1E) + Value: 0x8 + DF_BIND_NOW (0x8) + } + Dynamic { + Tag: DT_FLAGS_1 (0x6FFFFFFB) + Value: 0x8000001 + DF_1_NOW (0x1) + DF_1_PIE (0x8000000) + } + Dynamic { + Tag: DT_VERNEED (0x6FFFFFFE) + Value: 0x428 + } + Dynamic { + Tag: DT_VERNEEDNUM (0x6FFFFFFF) + Value: 0x1 + } + Dynamic { + Tag: DT_VERSYM (0x6FFFFFF0) + Value: 0x414 + } + Dynamic { + Tag: DT_RELACOUNT (0x6FFFFFF9) + Value: 0x3 + } + Dynamic { + Tag: DT_NULL (0x0) + Value: 0x0 + } +} +ProgramHeader { + Type: PT_NOTE (0x4) + Offset: 0x254 + VirtualAddress: 0x254 + PhysicalAddress: 0x254 + FileSize: 0x44 + MemorySize: 0x44 + Flags: 0x4 + PF_R (0x4) + Align: 0x4 + Note { + Name: "GNU" + Type: NT_GNU_ABI_TAG (0x1) + Desc: [0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0] + } + Note { + Name: "GNU" + Type: NT_GNU_BUILD_ID (0x3) + Desc: [D4, 46, A0, 61, BB, 9A, C2, 7A, B4, 3B, 11, 71, 8F, DE, DF, 5B, 7F, 3A, F6, F4] + } +} +ProgramHeader { + Type: PT_GNU_EH_FRAME (0x6474E550) + Offset: 0x734 + VirtualAddress: 0x734 + PhysicalAddress: 0x734 + FileSize: 0x3C + MemorySize: 0x3C + Flags: 0x4 + PF_R (0x4) + Align: 0x4 +} +ProgramHeader { + Type: PT_GNU_STACK (0x6474E551) + Offset: 0x0 + VirtualAddress: 0x0 + PhysicalAddress: 0x0 + FileSize: 0x0 + MemorySize: 0x0 + Flags: 0x6 + PF_W (0x2) + PF_R (0x4) + Align: 0x10 +} +ProgramHeader { + Type: PT_GNU_RELRO (0x6474E552) + Offset: 0xDA8 + VirtualAddress: 0x200DA8 + PhysicalAddress: 0x200DA8 + FileSize: 0x258 + MemorySize: 0x258 + Flags: 0x4 + PF_R (0x4) + Align: 0x1 +} +SectionHeader { + Index: 0 + Name: "" + Type: SHT_NULL (0x0) + Flags: 0x0 + Address: 0x0 + Offset: 0x0 + Size: 0x0 + Link: 0 + Info: 0 + AddressAlign: 0x0 + EntrySize: 0x0 +} +SectionHeader { + Index: 1 + Name: ".interp" + Type: SHT_PROGBITS (0x1) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x238 + Offset: 0x238 + Size: 0x1C + Link: 0 + Info: 0 + AddressAlign: 0x1 + EntrySize: 0x0 +} +SectionHeader { + Index: 2 + Name: ".note.ABI-tag" + Type: SHT_NOTE (0x7) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x254 + Offset: 0x254 + Size: 0x20 + Link: 0 + Info: 0 + AddressAlign: 0x4 + EntrySize: 0x0 + Note { + Name: "GNU" + Type: NT_GNU_ABI_TAG (0x1) + Desc: [0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0] + } +} +SectionHeader { + Index: 3 + Name: ".note.gnu.build-id" + Type: SHT_NOTE (0x7) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x274 + Offset: 0x274 + Size: 0x24 + Link: 0 + Info: 0 + AddressAlign: 0x4 + EntrySize: 0x0 + Note { + Name: "GNU" + Type: NT_GNU_BUILD_ID (0x3) + Desc: [D4, 46, A0, 61, BB, 9A, C2, 7A, B4, 3B, 11, 71, 8F, DE, DF, 5B, 7F, 3A, F6, F4] + } +} +SectionHeader { + Index: 4 + Name: ".hash" + Type: SHT_HASH (0x5) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x298 + Offset: 0x298 + Size: 0x30 + Link: 6 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x4 + Hash { + BucketCount: 3 + ChainCount: 7 + } +} +SectionHeader { + Index: 5 + Name: ".gnu.hash" + Type: SHT_GNU_HASH (0x6FFFFFF6) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x2C8 + Offset: 0x2C8 + Size: 0x1C + Link: 6 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x0 + GnuHash { + BucketCount: 1 + SymbolBase: 1 + BloomCount: 1 + BloomShift: 0 + } +} +SectionHeader { + Index: 6 + Name: ".dynsym" + Type: SHT_DYNSYM (0xB) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x2E8 + Offset: 0x2E8 + Size: 0xA8 + Link: 7 + Info: 1 + AddressAlign: 0x8 + EntrySize: 0x18 + Symbol { + Index: 0 + Name: "" + Version: VER_NDX_LOCAL (0x0) + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 1 + Name: "_ITM_deregisterTMCloneTable" + Version: VER_NDX_LOCAL (0x0) + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 2 + Name: "printf" + Version: "GLIBC_2.2.5" + Value: 0x0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 3 + Name: "__libc_start_main" + Version: "GLIBC_2.2.5" + Value: 0x0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 4 + Name: "__gmon_start__" + Version: VER_NDX_LOCAL (0x0) + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 5 + Name: "_ITM_registerTMCloneTable" + Version: VER_NDX_LOCAL (0x0) + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 6 + Name: "__cxa_finalize" + Version: "GLIBC_2.2.5" + Value: 0x0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } +} +SectionHeader { + Index: 7 + Name: ".dynstr" + Type: SHT_STRTAB (0x3) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x390 + Offset: 0x390 + Size: 0x84 + Link: 0 + Info: 0 + AddressAlign: 0x1 + EntrySize: 0x0 +} +SectionHeader { + Index: 8 + Name: ".gnu.version" + Type: SHT_GNU_VERSYM (0x6FFFFFFF) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x414 + Offset: 0x414 + Size: 0xE + Link: 6 + Info: 0 + AddressAlign: 0x2 + EntrySize: 0x2 + VersionSymbol { + Index: 0 + Version: VER_NDX_LOCAL (0x0) + } + VersionSymbol { + Index: 1 + Version: VER_NDX_LOCAL (0x0) + } + VersionSymbol { + Index: 2 + Version: "GLIBC_2.2.5" + } + VersionSymbol { + Index: 3 + Version: "GLIBC_2.2.5" + } + VersionSymbol { + Index: 4 + Version: VER_NDX_LOCAL (0x0) + } + VersionSymbol { + Index: 5 + Version: VER_NDX_LOCAL (0x0) + } + VersionSymbol { + Index: 6 + Version: "GLIBC_2.2.5" + } +} +SectionHeader { + Index: 9 + Name: ".gnu.version_r" + Type: SHT_GNU_VERNEED (0x6FFFFFFE) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x428 + Offset: 0x428 + Size: 0x20 + Link: 7 + Info: 1 + AddressAlign: 0x8 + EntrySize: 0x0 + VersionNeed { + Version: 1 + AuxCount: 1 + Filename: "libc.so.6" + AuxOffset: 16 + NextOffset: 0 + Aux { + Hash: 0x9691A75 + Flags: 0x0 + Index: 2 + Name: "GLIBC_2.2.5" + NextOffset: 0 + } + } +} +SectionHeader { + Index: 10 + Name: ".rela.dyn" + Type: SHT_RELA (0x4) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x448 + Offset: 0x448 + Size: 0xC0 + Link: 6 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x18 + Relocation { + Offset: 0x200DA8 + Type: R_X86_64_RELATIVE (0x8) + Symbol: "" + Addend: 0x670 + } + Relocation { + Offset: 0x200DB0 + Type: R_X86_64_RELATIVE (0x8) + Symbol: "" + Addend: 0x630 + } + Relocation { + Offset: 0x201008 + Type: R_X86_64_RELATIVE (0x8) + Symbol: "" + Addend: 0x201008 + } + Relocation { + Offset: 0x200FD8 + Type: R_X86_64_GLOB_DAT (0x6) + Symbol: "_ITM_deregisterTMCloneTable" + } + Relocation { + Offset: 0x200FE0 + Type: R_X86_64_GLOB_DAT (0x6) + Symbol: "__libc_start_main" + } + Relocation { + Offset: 0x200FE8 + Type: R_X86_64_GLOB_DAT (0x6) + Symbol: "__gmon_start__" + } + Relocation { + Offset: 0x200FF0 + Type: R_X86_64_GLOB_DAT (0x6) + Symbol: "_ITM_registerTMCloneTable" + } + Relocation { + Offset: 0x200FF8 + Type: R_X86_64_GLOB_DAT (0x6) + Symbol: "__cxa_finalize" + } +} +SectionHeader { + Index: 11 + Name: ".rela.plt" + Type: SHT_RELA (0x4) + Flags: 0x42 + SHF_ALLOC (0x2) + SHF_INFO_LINK (0x40) + Address: 0x508 + Offset: 0x508 + Size: 0x18 + Link: 6 + Info: 23 + AddressAlign: 0x8 + EntrySize: 0x18 + Relocation { + Offset: 0x200FD0 + Type: R_X86_64_JUMP_SLOT (0x7) + Symbol: "printf" + } +} +SectionHeader { + Index: 12 + Name: ".init" + Type: SHT_PROGBITS (0x1) + Flags: 0x6 + SHF_ALLOC (0x2) + SHF_EXECINSTR (0x4) + Address: 0x520 + Offset: 0x520 + Size: 0x17 + Link: 0 + Info: 0 + AddressAlign: 0x4 + EntrySize: 0x0 +} +SectionHeader { + Index: 13 + Name: ".plt" + Type: SHT_PROGBITS (0x1) + Flags: 0x6 + SHF_ALLOC (0x2) + SHF_EXECINSTR (0x4) + Address: 0x540 + Offset: 0x540 + Size: 0x20 + Link: 0 + Info: 0 + AddressAlign: 0x10 + EntrySize: 0x10 +} +SectionHeader { + Index: 14 + Name: ".plt.got" + Type: SHT_PROGBITS (0x1) + Flags: 0x6 + SHF_ALLOC (0x2) + SHF_EXECINSTR (0x4) + Address: 0x560 + Offset: 0x560 + Size: 0x8 + Link: 0 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x8 +} +SectionHeader { + Index: 15 + Name: ".text" + Type: SHT_PROGBITS (0x1) + Flags: 0x6 + SHF_ALLOC (0x2) + SHF_EXECINSTR (0x4) + Address: 0x570 + Offset: 0x570 + Size: 0x1A2 + Link: 0 + Info: 0 + AddressAlign: 0x10 + EntrySize: 0x0 +} +SectionHeader { + Index: 16 + Name: ".fini" + Type: SHT_PROGBITS (0x1) + Flags: 0x6 + SHF_ALLOC (0x2) + SHF_EXECINSTR (0x4) + Address: 0x714 + Offset: 0x714 + Size: 0x9 + Link: 0 + Info: 0 + AddressAlign: 0x4 + EntrySize: 0x0 +} +SectionHeader { + Index: 17 + Name: ".rodata" + Type: SHT_PROGBITS (0x1) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x720 + Offset: 0x720 + Size: 0x11 + Link: 0 + Info: 0 + AddressAlign: 0x4 + EntrySize: 0x0 +} +SectionHeader { + Index: 18 + Name: ".eh_frame_hdr" + Type: SHT_PROGBITS (0x1) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x734 + Offset: 0x734 + Size: 0x3C + Link: 0 + Info: 0 + AddressAlign: 0x4 + EntrySize: 0x0 +} +SectionHeader { + Index: 19 + Name: ".eh_frame" + Type: SHT_PROGBITS (0x1) + Flags: 0x2 + SHF_ALLOC (0x2) + Address: 0x770 + Offset: 0x770 + Size: 0x108 + Link: 0 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x0 +} +SectionHeader { + Index: 20 + Name: ".init_array" + Type: SHT_INIT_ARRAY (0xE) + Flags: 0x3 + SHF_WRITE (0x1) + SHF_ALLOC (0x2) + Address: 0x200DA8 + Offset: 0xDA8 + Size: 0x8 + Link: 0 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x8 +} +SectionHeader { + Index: 21 + Name: ".fini_array" + Type: SHT_FINI_ARRAY (0xF) + Flags: 0x3 + SHF_WRITE (0x1) + SHF_ALLOC (0x2) + Address: 0x200DB0 + Offset: 0xDB0 + Size: 0x8 + Link: 0 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x8 +} +SectionHeader { + Index: 22 + Name: ".dynamic" + Type: SHT_DYNAMIC (0x6) + Flags: 0x3 + SHF_WRITE (0x1) + SHF_ALLOC (0x2) + Address: 0x200DB8 + Offset: 0xDB8 + Size: 0x1C0 + Link: 7 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x10 + Dynamic { + Tag: DT_NEEDED (0x1) + Value: "libc.so.6" + } + Dynamic { + Tag: DT_INIT (0xC) + Value: 0x520 + } + Dynamic { + Tag: DT_FINI (0xD) + Value: 0x714 + } + Dynamic { + Tag: DT_INIT_ARRAY (0x19) + Value: 0x200DA8 + } + Dynamic { + Tag: DT_INIT_ARRAYSZ (0x1B) + Value: 0x8 + } + Dynamic { + Tag: DT_FINI_ARRAY (0x1A) + Value: 0x200DB0 + } + Dynamic { + Tag: DT_FINI_ARRAYSZ (0x1C) + Value: 0x8 + } + Dynamic { + Tag: DT_HASH (0x4) + Value: 0x298 + } + Dynamic { + Tag: DT_GNU_HASH (0x6FFFFEF5) + Value: 0x2C8 + } + Dynamic { + Tag: DT_STRTAB (0x5) + Value: 0x390 + } + Dynamic { + Tag: DT_SYMTAB (0x6) + Value: 0x2E8 + } + Dynamic { + Tag: DT_STRSZ (0xA) + Value: 0x84 + } + Dynamic { + Tag: DT_SYMENT (0xB) + Value: 0x18 + } + Dynamic { + Tag: DT_DEBUG (0x15) + Value: 0x0 + } + Dynamic { + Tag: DT_PLTGOT (0x3) + Value: 0x200FB8 + } + Dynamic { + Tag: DT_PLTRELSZ (0x2) + Value: 0x18 + } + Dynamic { + Tag: DT_PLTREL (0x14) + Value: 0x7 + } + Dynamic { + Tag: DT_JMPREL (0x17) + Value: 0x508 + } + Dynamic { + Tag: DT_RELA (0x7) + Value: 0x448 + } + Dynamic { + Tag: DT_RELASZ (0x8) + Value: 0xC0 + } + Dynamic { + Tag: DT_RELAENT (0x9) + Value: 0x18 + } + Dynamic { + Tag: DT_FLAGS (0x1E) + Value: 0x8 + DF_BIND_NOW (0x8) + } + Dynamic { + Tag: DT_FLAGS_1 (0x6FFFFFFB) + Value: 0x8000001 + DF_1_NOW (0x1) + DF_1_PIE (0x8000000) + } + Dynamic { + Tag: DT_VERNEED (0x6FFFFFFE) + Value: 0x428 + } + Dynamic { + Tag: DT_VERNEEDNUM (0x6FFFFFFF) + Value: 0x1 + } + Dynamic { + Tag: DT_VERSYM (0x6FFFFFF0) + Value: 0x414 + } + Dynamic { + Tag: DT_RELACOUNT (0x6FFFFFF9) + Value: 0x3 + } + Dynamic { + Tag: DT_NULL (0x0) + Value: 0x0 + } +} +SectionHeader { + Index: 23 + Name: ".got" + Type: SHT_PROGBITS (0x1) + Flags: 0x3 + SHF_WRITE (0x1) + SHF_ALLOC (0x2) + Address: 0x200FB8 + Offset: 0xFB8 + Size: 0x48 + Link: 0 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x8 +} +SectionHeader { + Index: 24 + Name: ".data" + Type: SHT_PROGBITS (0x1) + Flags: 0x3 + SHF_WRITE (0x1) + SHF_ALLOC (0x2) + Address: 0x201000 + Offset: 0x1000 + Size: 0x10 + Link: 0 + Info: 0 + AddressAlign: 0x8 + EntrySize: 0x0 +} +SectionHeader { + Index: 25 + Name: ".bss" + Type: SHT_NOBITS (0x8) + Flags: 0x3 + SHF_WRITE (0x1) + SHF_ALLOC (0x2) + Address: 0x201010 + Offset: 0x1010 + Size: 0x8 + Link: 0 + Info: 0 + AddressAlign: 0x1 + EntrySize: 0x0 +} +SectionHeader { + Index: 26 + Name: ".comment" + Type: SHT_PROGBITS (0x1) + Flags: 0x30 + SHF_MERGE (0x10) + SHF_STRINGS (0x20) + Address: 0x0 + Offset: 0x1010 + Size: 0x29 + Link: 0 + Info: 0 + AddressAlign: 0x1 + EntrySize: 0x1 +} +SectionHeader { + Index: 27 + Name: ".symtab" + Type: SHT_SYMTAB (0x2) + Flags: 0x0 + Address: 0x0 + Offset: 0x1040 + Size: 0x600 + Link: 28 + Info: 44 + AddressAlign: 0x8 + EntrySize: 0x18 + Symbol { + Index: 0 + Name: "" + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 1 + Name: "" + Value: 0x238 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 1 + } + Symbol { + Index: 2 + Name: "" + Value: 0x254 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 2 + } + Symbol { + Index: 3 + Name: "" + Value: 0x274 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 3 + } + Symbol { + Index: 4 + Name: "" + Value: 0x298 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 4 + } + Symbol { + Index: 5 + Name: "" + Value: 0x2C8 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 5 + } + Symbol { + Index: 6 + Name: "" + Value: 0x2E8 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 6 + } + Symbol { + Index: 7 + Name: "" + Value: 0x390 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 7 + } + Symbol { + Index: 8 + Name: "" + Value: 0x414 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 8 + } + Symbol { + Index: 9 + Name: "" + Value: 0x428 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 9 + } + Symbol { + Index: 10 + Name: "" + Value: 0x448 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 10 + } + Symbol { + Index: 11 + Name: "" + Value: 0x508 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 11 + } + Symbol { + Index: 12 + Name: "" + Value: 0x520 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 12 + } + Symbol { + Index: 13 + Name: "" + Value: 0x540 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 13 + } + Symbol { + Index: 14 + Name: "" + Value: 0x560 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 14 + } + Symbol { + Index: 15 + Name: "" + Value: 0x570 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 16 + Name: "" + Value: 0x714 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 16 + } + Symbol { + Index: 17 + Name: "" + Value: 0x720 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 17 + } + Symbol { + Index: 18 + Name: "" + Value: 0x734 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 18 + } + Symbol { + Index: 19 + Name: "" + Value: 0x770 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 19 + } + Symbol { + Index: 20 + Name: "" + Value: 0x200DA8 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 20 + } + Symbol { + Index: 21 + Name: "" + Value: 0x200DB0 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 21 + } + Symbol { + Index: 22 + Name: "" + Value: 0x200DB8 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 22 + } + Symbol { + Index: 23 + Name: "" + Value: 0x200FB8 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 23 + } + Symbol { + Index: 24 + Name: "" + Value: 0x201000 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 24 + } + Symbol { + Index: 25 + Name: "" + Value: 0x201010 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 25 + } + Symbol { + Index: 26 + Name: "" + Value: 0x0 + Size: 0x0 + Type: STT_SECTION (0x3) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 26 + } + Symbol { + Index: 27 + Name: "crtstuff.c" + Value: 0x0 + Size: 0x0 + Type: STT_FILE (0x4) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_ABS (0xFFF1) + } + Symbol { + Index: 28 + Name: "deregister_tm_clones" + Value: 0x5A0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 29 + Name: "register_tm_clones" + Value: 0x5E0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 30 + Name: "__do_global_dtors_aux" + Value: 0x630 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 31 + Name: "completed.7698" + Value: 0x201010 + Size: 0x1 + Type: STT_OBJECT (0x1) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 25 + } + Symbol { + Index: 32 + Name: "__do_global_dtors_aux_fini_array_entry" + Value: 0x200DB0 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 21 + } + Symbol { + Index: 33 + Name: "frame_dummy" + Value: 0x670 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 34 + Name: "__frame_dummy_init_array_entry" + Value: 0x200DA8 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 20 + } + Symbol { + Index: 35 + Name: "base.c" + Value: 0x0 + Size: 0x0 + Type: STT_FILE (0x4) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_ABS (0xFFF1) + } + Symbol { + Index: 36 + Name: "crtstuff.c" + Value: 0x0 + Size: 0x0 + Type: STT_FILE (0x4) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_ABS (0xFFF1) + } + Symbol { + Index: 37 + Name: "__FRAME_END__" + Value: 0x874 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 19 + } + Symbol { + Index: 38 + Name: "" + Value: 0x0 + Size: 0x0 + Type: STT_FILE (0x4) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_ABS (0xFFF1) + } + Symbol { + Index: 39 + Name: "__init_array_end" + Value: 0x200DB0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 20 + } + Symbol { + Index: 40 + Name: "_DYNAMIC" + Value: 0x200DB8 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 22 + } + Symbol { + Index: 41 + Name: "__init_array_start" + Value: 0x200DA8 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 20 + } + Symbol { + Index: 42 + Name: "__GNU_EH_FRAME_HDR" + Value: 0x734 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 18 + } + Symbol { + Index: 43 + Name: "_GLOBAL_OFFSET_TABLE_" + Value: 0x200FB8 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_LOCAL (0x0) + Other: STV_DEFAULT (0x0) + SectionIndex: 23 + } + Symbol { + Index: 44 + Name: "__libc_csu_fini" + Value: 0x710 + Size: 0x2 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 45 + Name: "_ITM_deregisterTMCloneTable" + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 46 + Name: "data_start" + Value: 0x201000 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: 24 + } + Symbol { + Index: 47 + Name: "_edata" + Value: 0x201010 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 24 + } + Symbol { + Index: 48 + Name: "_fini" + Value: 0x714 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 16 + } + Symbol { + Index: 49 + Name: "printf@@GLIBC_2.2.5" + Value: 0x0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 50 + Name: "__libc_start_main@@GLIBC_2.2.5" + Value: 0x0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 51 + Name: "__data_start" + Value: 0x201000 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 24 + } + Symbol { + Index: 52 + Name: "__gmon_start__" + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 53 + Name: "__dso_handle" + Value: 0x201008 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_GLOBAL (0x1) + Other: STV_HIDDEN (0x2) + SectionIndex: 24 + } + Symbol { + Index: 54 + Name: "_IO_stdin_used" + Value: 0x720 + Size: 0x4 + Type: STT_OBJECT (0x1) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 17 + } + Symbol { + Index: 55 + Name: "__libc_csu_init" + Value: 0x6A0 + Size: 0x65 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 56 + Name: "_end" + Value: 0x201018 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 25 + } + Symbol { + Index: 57 + Name: "_start" + Value: 0x570 + Size: 0x2B + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 58 + Name: "__bss_start" + Value: 0x201010 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 25 + } + Symbol { + Index: 59 + Name: "main" + Value: 0x67A + Size: 0x1C + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 15 + } + Symbol { + Index: 60 + Name: "__TMC_END__" + Value: 0x201010 + Size: 0x0 + Type: STT_OBJECT (0x1) + Bind: STB_GLOBAL (0x1) + Other: STV_HIDDEN (0x2) + SectionIndex: 24 + } + Symbol { + Index: 61 + Name: "_ITM_registerTMCloneTable" + Value: 0x0 + Size: 0x0 + Type: STT_NOTYPE (0x0) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 62 + Name: "__cxa_finalize@@GLIBC_2.2.5" + Value: 0x0 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_WEAK (0x2) + Other: STV_DEFAULT (0x0) + SectionIndex: SHN_UNDEF (0x0) + } + Symbol { + Index: 63 + Name: "_init" + Value: 0x520 + Size: 0x0 + Type: STT_FUNC (0x2) + Bind: STB_GLOBAL (0x1) + Other: STV_DEFAULT (0x0) + SectionIndex: 12 + } +} +SectionHeader { + Index: 28 + Name: ".strtab" + Type: SHT_STRTAB (0x3) + Flags: 0x0 + Address: 0x0 + Offset: 0x1640 + Size: 0x204 + Link: 0 + Info: 0 + AddressAlign: 0x1 + EntrySize: 0x0 +} +SectionHeader { + Index: 29 + Name: ".shstrtab" + Type: SHT_STRTAB (0x3) + Flags: 0x0 + Address: 0x0 + Offset: 0x1844 + Size: 0xFE + Link: 0 + Info: 0 + AddressAlign: 0x1 + EntrySize: 0x0 +} diff --git a/crates/examples/tests/testfiles.rs b/crates/examples/tests/testfiles.rs index 89f8b920..e50dbff6 100644 --- a/crates/examples/tests/testfiles.rs +++ b/crates/examples/tests/testfiles.rs @@ -8,6 +8,7 @@ use std::path::{Path, PathBuf}; use std::{env, fs}; const DISABLED_TEST_DIRS: &[&'static str] = &[ + "rewrite", #[cfg(not(feature = "wasm"))] "wasm", #[cfg(not(feature = "xcoff"))] @@ -21,9 +22,13 @@ fn test_dir_enabled(path: &Path) -> bool { } } +fn is_update() -> bool { + env::var_os("OBJECT_TESTFILES_UPDATE").is_some() +} + #[test] fn testfiles() { - let update = env::var_os("OBJECT_TESTFILES_UPDATE").is_some(); + let update = is_update(); let in_testfiles = PathBuf::from("../.."); let out_testfiles = PathBuf::from("testfiles"); @@ -62,20 +67,30 @@ fn testfiles() { None }; + println!("Test {}", out_path.display()); + let in_data = match fs::read(&in_path) { + Ok(in_data) => in_data, + Err(err) => { + println!("FAIL Couldn't read {}: {}", in_path.display(), err); + fail = true; + continue; + } + }; + if extension == "objdump" { - fail |= testfile(update, &in_path, &out_path, err_path, |out, err, data| { + fail |= testfile(update, &in_data, &out_path, err_path, |out, err, data| { objdump::print(out, err, data, &[], vec![]).unwrap(); }); } else if extension == "objdump-shndx" { // Special case for the large symtab shndx test. - fail |= testfile(update, &in_path, &out_path, err_path, |out, err, data| { + fail |= testfile(update, &in_data, &out_path, err_path, |out, err, data| { objdump::print(out, err, data, &[], vec![]).unwrap(); *out = filter_lines(&*out, |line| { line.starts_with("6553") && line[5..].starts_with(": Symbol {") }); }); } else if extension == "objdump-comdat" { - fail |= testfile(update, &in_path, &out_path, err_path, |out, err, data| { + fail |= testfile(update, &in_data, &out_path, err_path, |out, err, data| { objdump::print(out, err, data, &[], vec![]).unwrap(); *out = filter_lines(&*out, |line| line.starts_with("Comdat ")); }); @@ -107,7 +122,7 @@ fn testfiles() { continue; } }; - fail |= testfile(update, &in_path, &out_path, err_path, |out, err, data| { + fail |= testfile(update, &in_data, &out_path, err_path, |out, err, data| { readobj::print(out, err, data, &options); }); } else if extension == "objcopy" { @@ -115,7 +130,7 @@ fn testfiles() { { fail |= testfile( update, - &in_path, + &in_data, &out_path, err_path, |out, err, in_data| { @@ -135,24 +150,61 @@ fn testfiles() { } } -fn testfile(update: bool, in_path: &Path, out_path: &Path, err_path: Option<&Path>, f: F) -> bool +#[cfg(feature = "copy")] +#[test] +fn testfiles_rewrite() { + let mut fail = false; + + fail |= testfile_rewrite("elf/base", "elf/base-noop", |_builder| {}); + + if fail { + panic!("Tests failed; rerun with OBJECT_TESTFILES_UPDATE=1 to update tests"); + } +} + +#[cfg(feature = "copy")] +fn testfile_rewrite(in_path: &str, out_path: &str, f: F) -> bool where - F: FnOnce(&mut Vec, &mut Vec, &[u8]), + F: FnOnce(&mut object::copy::elf::Builder), { + let update = is_update(); + + let in_path = Path::new("../../testfiles").join(in_path); + let out_path = Path::new("testfiles/rewrite").join(out_path); + println!("Test {}", out_path.display()); - let source = match fs::read(in_path) { - Ok(source) => source, + let in_data = match fs::read(&in_path) { + Ok(in_data) => in_data, Err(err) => { - println!("FAIL {}", err); + println!("FAIL Couldn't read {}: {}", in_path.display(), err); return true; } }; - // TODO: print diffs for mismatches - let mut fail = false; + let mut builder = object::copy::elf::Builder::read(&*in_data).unwrap(); + f(&mut builder); + let mut rewrite_data = Vec::new(); + builder.write(&mut rewrite_data).unwrap(); + + let options = readobj::PrintOptions { + string_indices: false, + ..readobj::PrintOptions::all() + }; + testfile(update, &rewrite_data, &out_path, None, |out, err, data| { + readobj::print(out, err, data, &options); + }) +} + +fn testfile(update: bool, in_data: &[u8], out_path: &Path, err_path: Option<&Path>, f: F) -> bool +where + F: FnOnce(&mut Vec, &mut Vec, &[u8]), +{ let mut out_data = Vec::new(); let mut err_data = Vec::new(); - f(&mut out_data, &mut err_data, &source); + f(&mut out_data, &mut err_data, &in_data); + + // TODO: print diffs for mismatches + let mut fail = false; // Check exact match of output. if update { diff --git a/src/copy/elf.rs b/src/copy/elf.rs new file mode 100644 index 00000000..ce2be25a --- /dev/null +++ b/src/copy/elf.rs @@ -0,0 +1,2909 @@ +//! Builder for reading, modifying, and then writing ELF files. +use alloc::string::String; +use alloc::vec::Vec; +use core::convert::TryInto; +use core::marker::PhantomData; +use hashbrown::HashMap; + +use crate::copy::{Error, Result}; +use crate::elf; +use crate::read::elf::{Dyn, FileHeader, ProgramHeader, Rela, SectionHeader, Sym}; +use crate::read::{self, FileKind, ReadRef}; +use crate::write; +use crate::Endianness; + +/// A builder for reading, modifying, and then writing ELF files. +/// +/// Public fields are available for modifying the values that will be written. +/// Some of these correspond to fields in [`elf::FileHeader32`] or [`elf::FileHeader64`]. +/// +/// Methods are available to add elements to tables, and elements can be deleted +/// from tables by setting the `delete` field in the element. +#[derive(Debug)] +pub struct Builder<'data> { + /// The endianness. + /// + /// Used to set the data encoding when writing the ELF file. + pub endian: Endianness, + /// Whether file is 64-bit. + /// + /// Use to set the file class when writing the ELF file. + pub is_64: bool, + /// The alignment of [`elf::PT_LOAD`] segments. + pub load_align: u64, + /// The OS ABI field in the file header. + /// + /// One of the `ELFOSABI*` constants. + pub os_abi: u8, + /// The ABI version field in the file header. + /// + /// The meaning of this field depends on the `os_abi` value. + pub abi_version: u8, + /// The object file type in the file header. + /// + /// One of the `ET_*` constants. + pub e_type: u16, + /// The architecture in the file header. + /// + /// One of the `EM_*` constants. + pub e_machine: u16, + /// Entry point virtual address in the file header. + pub e_entry: u64, + /// The processor-specific flags in the file header. + /// + /// A combination of the `EF_*` constants. + pub e_flags: u32, + /// The file offset of the program header table. + /// + /// Writing will fail if the program header table cannot be placed at this offset. + pub e_phoff: u64, + /// The segment table. + pub segments: Segments<'data>, + /// The section table. + pub sections: Sections<'data>, + /// The symbol table. + pub symbols: Symbols<'data>, + /// The dynamic symbol table. + pub dynamic_symbols: Symbols<'data>, + /// The base version for the GNU version definitions. + /// + /// This will be written as a version definition with index 1. + pub version_base: Option<&'data [u8]>, + /// The GNU version definitions and dependencies. + pub versions: Versions<'data>, + /// The filenames used in the GNU version definitions. + pub version_files: VersionFiles<'data>, + /// The bucket count parameter for the hash table. + pub hash_bucket_count: u32, + /// The bloom shift parameter for the GNU hash table. + pub gnu_hash_bloom_shift: u32, + /// The bloom count parameter for the GNU hash table. + pub gnu_hash_bloom_count: u32, + /// The bucket count parameter for the GNU hash table. + pub gnu_hash_bucket_count: u32, + /// The GNU attributes. + pub gnu_attributes: AttributesSection<'data>, + marker: PhantomData<()>, +} + +impl<'data> Builder<'data> { + /// Read the ELF file from file data. + 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("Not an ELF file".into())), + } + } + + 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 mut builder = Builder { + endian, + is_64: header.is_type_64(), + load_align: 0, + 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(), + segments: Segments::new(), + sections: Sections::new(), + symbols: Symbols::new(false), + dynamic_symbols: Symbols::new(true), + 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(), + marker: PhantomData, + }; + + for segment in segments { + if segment.p_type(endian) == elf::PT_LOAD { + let p_align = segment.p_align(endian).into(); + if builder.load_align != 0 && builder.load_align != p_align { + return Err(Error("Unsupported alignments for PT_LOAD segments".into())); + } + builder.load_align = p_align; + } + + let id = SegmentId(builder.segments.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(), + sections: Vec::new(), + delete: false, + marker: PhantomData, + }); + } + if !builder.segments.is_empty() && builder.load_align == 0 { + // There should be at least one PT_LOAD segment. + return Err(Error( + "Unsupported segments without for PT_LOAD segment".into(), + )); + } + + for (index, section) in sections.iter().enumerate().skip(1) { + let id = SectionId(index - 1); + let relocations = if let Some((rels, link)) = section.rel(endian, data)? { + Self::read_relocations( + index, + endian, + is_mips64el, + rels, + link, + &symbols, + &dynamic_symbols, + )? + } else if let Some((rels, link)) = section.rela(endian, data)? { + Self::read_relocations( + index, + endian, + is_mips64el, + rels, + link, + &symbols, + &dynamic_symbols, + )? + } else { + Vec::new() + }; + let dynamics = if let Some((dyns, link)) = section.dynamic(endian, data)? { + let dynamic_strings = sections.strings(endian, data, link)?; + Self::read_dynamics::(endian, dyns, dynamic_strings)? + } else { + Vec::new() + }; + 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)? { + builder.read_gnu_attributes( + index, + attributes, + sections.len(), + dynamic_symbols.len(), + )?; + } + let data = match section.sh_type(endian) { + elf::SHT_NOBITS => SectionData::UninitializedData(section.sh_size(endian).into()), + elf::SHT_PROGBITS | elf::SHT_INIT_ARRAY | elf::SHT_FINI_ARRAY => { + SectionData::Data(section.data(endian, data)?) + } + elf::SHT_REL | elf::SHT_RELA => SectionData::Relocation(relocations), + 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 == 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_NOTE => SectionData::Note(section.data(endian, data)?), + elf::SHT_DYNAMIC => SectionData::Dynamic(dynamics), + 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 { + if sh_link as usize >= sections.len() { + return Err(Error(format!( + "Invalid sh_link {} in section at index {}", + sh_link, index + ))); + } + 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 { + if sh_info as usize >= sections.len() { + return Err(Error(format!( + "Invalid sh_info link {} in section at index {}", + sh_info, index + ))); + } + Some(SectionId(sh_info as usize - 1)) + }; + let sh_flags = section.sh_flags(endian).into(); + let sh_addr = section.sh_addr(endian).into(); + if sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + for segment in &mut builder.segments { + if segment.contains_address(sh_addr) { + segment.sections.push(id); + } + } + } + builder.sections.0.push(Section { + id, + name: sections.section_name(endian, section)?, + sh_type: section.sh_type(endian), + sh_flags, + sh_addr, + sh_offset: section.sh_offset(endian).into(), + sh_size: section.sh_size(endian).into(), + sh_link_section, + sh_info, + sh_info_section, + sh_addralign: section.sh_addralign(endian).into(), + sh_entsize: section.sh_entsize(endian).into(), + data, + delete: false, + }); + } + + builder.read_symbols(endian, &symbols, false)?; + builder.read_symbols(endian, &dynamic_symbols, true)?; + builder.read_gnu_versions(endian, data, §ions, &dynamic_symbols)?; + + Ok(builder) + } + + #[allow(clippy::too_many_arguments)] + fn read_relocations( + index: usize, + endian: Elf::Endian, + is_mips64el: bool, + rels: &'data [Rel], + link: read::SectionIndex, + symbols: &read::elf::SymbolTable<'data, Elf, R>, + dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>, + ) -> Result> + where + Elf: FileHeader, + Rel: Copy + Into, + R: ReadRef<'data>, + { + let mut relocations = Vec::new(); + let (symbols_len, dynamic) = if link.0 == 0 { + (0, true) + } else if link == symbols.section() { + (symbols.len(), false) + } else if link == dynamic_symbols.section() { + (dynamic_symbols.len(), true) + } else { + return Err(Error(format!( + "Invalid sh_link {} in relocation section at index {}", + link.0, index, + ))); + }; + for rel in rels { + let rel = (*rel).into(); + let r_sym = rel.r_sym(endian, is_mips64el); + let symbol = if r_sym == 0 { + None + } else { + if r_sym as usize >= symbols_len { + return Err(Error(format!( + "Invalid symbol index {} in relocation section at index {}", + r_sym, index, + ))); + } + Some(SymbolId { + index: r_sym as usize - 1, + dynamic, + }) + }; + relocations.push(Relocation { + r_offset: rel.r_offset(endian).into(), + symbol, + r_type: rel.r_type(endian, is_mips64el), + r_addend: rel.r_addend(endian).into(), + }); + } + Ok(relocations) + } + + fn read_dynamics( + endian: Elf::Endian, + dyns: &'data [Elf::Dyn], + strings: read::StringTable<'data, R>, + ) -> Result>> + where + Elf: FileHeader, + R: ReadRef<'data>, + { + let mut dynamics = Vec::with_capacity(dyns.len()); + 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(); + dynamics.push(if d.is_string(endian) { + let val = + 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 { + match tag { + elf::DT_NULL + | elf::DT_HASH + | elf::DT_GNU_HASH + | elf::DT_STRTAB + | elf::DT_STRSZ + | elf::DT_SYMTAB + | elf::DT_VERSYM + | elf::DT_VERDEF + | elf::DT_VERDEFNUM + | elf::DT_VERNEED + | elf::DT_VERNEEDNUM => Dynamic::Auto { tag }, + _ => Dynamic::Integer { tag, val }, + } + }); + if tag == elf::DT_NULL { + break; + } + } + Ok(dynamics) + } + + fn read_symbols( + &mut self, + endian: Elf::Endian, + symbols: &read::elf::SymbolTable<'data, Elf, R>, + dynamic: bool, + ) -> Result<()> + where + Elf: FileHeader, + R: ReadRef<'data>, + { + let builder_symbols = if dynamic { + &mut self.dynamic_symbols + } else { + &mut self.symbols + }; + for (index, symbol) in symbols.iter().enumerate().skip(1) { + let id = SymbolId { + index: index - 1, + dynamic, + }; + let section = + if let Some(section_index) = symbols.symbol_section(endian, symbol, index)? { + let section_id = section_index.0.wrapping_sub(1); + if section_id >= self.sections.len() { + return Err(Error("Invalid symbol section index".into())); + } + Some(SectionId(section_id)) + } else { + None + }; + builder_symbols.v.push(Symbol { + id, + name: symbols.symbol_name(endian, symbol)?, + section, + 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::local(), + version_hidden: false, + delete: false, + }); + } + Ok(()) + } + + fn read_gnu_attributes( + &mut self, + index: usize, + attributes: read::elf::AttributesSection<'data, Elf>, + sections_len: usize, + symbols_len: usize, + ) -> Result<()> + where + Elf: FileHeader, + { + 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 tag_sections = Vec::new(); + let mut indices = subsubsection.indices(); + while let Some(index) = indices.next()? { + let index = index as usize; + if index >= sections_len { + return Err(Error(format!( + "Invalid section index {} in attribute", + index + ))); + } + tag_sections.push(SectionId(index - 1)); + } + AttributeTag::Section(tag_sections) + } + elf::Tag_Symbol => { + let mut tag_symbols = Vec::new(); + let mut indices = subsubsection.indices(); + while let Some(index) = indices.next()? { + let index = index as usize; + // TODO: not sure if these dynamic or not + if index >= symbols_len { + return Err(Error(format!( + "Invalid symbol index {} in attribute", + index + ))); + } + tag_symbols.push(SymbolId { + index: index - 1, + dynamic: true, + }); + } + AttributeTag::Symbol(tag_symbols) + } + tag => { + return Err(Error(format!( + "Unsupported attribute tag 0x{:x} in section at index {}", + tag, index, + ))) + } + }; + let data = subsubsection.attributes_data(); + builder_subsection + .subsubsections + .push(AttributesSubsubsection { tag, data }); + } + self.gnu_attributes.subsections.push(builder_subsection); + } + Ok(()) + } + + fn read_gnu_versions( + &mut self, + endian: Elf::Endian, + data: R, + sections: &read::elf::SectionTable<'data, Elf, R>, + dynamic_symbols: &read::elf::SymbolTable<'data, Elf, R>, + ) -> Result<()> + where + Elf: FileHeader, + R: ReadRef<'data>, + { + let strings = dynamic_symbols.strings(); + let mut ids = HashMap::new(); + ids.insert(0, VersionId::local()); + ids.insert(1, VersionId::global()); + + if let Some((mut verdefs, link)) = sections.gnu_verdef(endian, data)? { + if link != dynamic_symbols.string_section() { + return Err(Error("Invalid SHT_GNU_VERDEF section".into())); + } + 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("Unsupported VER_FLG_BASE in SHT_GNU_VERDEF".into())); + } + if self.version_base.is_some() { + return Err(Error("Duplicate VER_FLG_BASE in SHT_GNU_VERDEF".into())); + } + let verdaux = verdauxs.next()?.ok_or_else(|| { + Error("Missing name for VER_FLG_BASE in SHT_GNU_VERDEF".into()) + })?; + self.version_base = Some(verdaux.name(endian, strings)?); + continue; + } + + let index = verdef.vd_ndx.get(endian) & elf::VERSYM_VERSION; + let id = VersionId(self.versions.len() + VERSION_ID_BASE); + if ids.insert(index, id).is_some() { + return Err(Error(format!("Duplicate SHT_GNU_VERDEF index {}", index))); + } + + let mut names = Vec::new(); + while let Some(verdaux) = verdauxs.next()? { + names.push(verdaux.name(endian, strings)?); + } + + let data = VersionData::Def(VersionDef { flags, names }); + self.versions.0.push(Version { + id, + data, + delete: false, + }); + } + } + + if let Some((mut verneeds, link)) = sections.gnu_verneed(endian, data)? { + if link != dynamic_symbols.string_section() { + return Err(Error("Invalid SHT_GNU_VERNEED section".into())); + } + while let Some((verneed, mut vernauxs)) = verneeds.next()? { + let file = VersionFileId(self.version_files.len()); + self.version_files.0.push(VersionFile { + id: file, + name: verneed.file(endian, strings)?, + delete: false, + }); + while let Some(vernaux) = vernauxs.next()? { + let index = vernaux.vna_other.get(endian) & elf::VERSYM_VERSION; + let id = VersionId(self.versions.len() + VERSION_ID_BASE); + if ids.insert(index, id).is_some() { + return Err(Error(format!("Duplicate SHT_GNU_VERNEED index {}", index))); + } + + let data = VersionData::Need(VersionNeed { + flags: vernaux.vna_flags.get(endian), + name: vernaux.name(endian, strings)?, + file, + }); + self.versions.0.push(Version { + id, + data, + delete: false, + }); + } + } + } + + if let Some((versyms, link)) = sections.gnu_versym(endian, data)? { + if versyms.len() != dynamic_symbols.len() || link != dynamic_symbols.section() { + return Err(Error("Invalid SHT_GNU_VERSYM section".into())); + } + for (id, versym) in versyms.iter().skip(1).enumerate() { + let index = versym.0.get(endian); + let symbol = &mut self.dynamic_symbols.v[id]; + symbol.version = *ids + .get(&(index & elf::VERSYM_VERSION)) + .ok_or_else(|| Error(format!("Invalid SHT_GNU_VERSYM index {:x}", index)))?; + symbol.version_hidden = index & elf::VERSYM_HIDDEN != 0; + } + } + Ok(()) + } + + /// Write the ELF file to the buffer. + pub fn write(mut self, buffer: &mut dyn write::WritableBuffer) -> Result<()> { + struct SectionOut { + id: SectionId, + name: 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, + } + + self.delete_orphan_symbols(); + self.delete_orphan_relocations(); + 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 gnu_attributes_id = None; + let mut out_sections = Vec::with_capacity(self.sections.len()); + let mut out_sections_index = Vec::with_capacity(self.sections.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(_) + | SectionData::Note(_) + | SectionData::Dynamic(_) => writer.reserve_section_index(), + SectionData::SectionString => { + if shstrtab_id.is_some() { + return Err(Error("Multiple .shstrtab sections".into())); + } + shstrtab_id = Some(section.id); + writer.reserve_shstrtab_section_index(Some(section.name)) + } + SectionData::Symbol => { + if symtab_id.is_some() { + return Err(Error("Multiple .symtab sections".into())); + } + symtab_id = Some(section.id); + writer.reserve_symtab_section_index(Some(section.name)) + } + SectionData::SymbolSectionIndex => { + if symtab_shndx_id.is_some() { + return Err(Error("Multiple .symtab_shndx sections".into())); + } + symtab_shndx_id = Some(section.id); + writer.reserve_symtab_shndx_section_index(Some(section.name)) + } + SectionData::String => { + if strtab_id.is_some() { + return Err(Error("Multiple .strtab sections".into())); + } + strtab_id = Some(section.id); + writer.reserve_strtab_section_index(Some(section.name)) + } + SectionData::DynamicSymbol => { + if dynsym_id.is_some() { + return Err(Error("Multiple .dynsym sections".into())); + } + dynsym_id = Some(section.id); + writer.reserve_dynsym_section_index(Some(section.name)) + } + SectionData::DynamicString => { + if dynstr_id.is_some() { + return Err(Error("Multiple .dynstr sections".into())); + } + dynstr_id = Some(section.id); + writer.reserve_dynstr_section_index(Some(section.name)) + } + SectionData::Hash => { + if hash_id.is_some() { + return Err(Error("Multiple .hash sections".into())); + } + hash_id = Some(section.id); + writer.reserve_hash_section_index(Some(section.name)) + } + SectionData::GnuHash => { + if gnu_hash_id.is_some() { + return Err(Error("Multiple .gnu.hash sections".into())); + } + gnu_hash_id = Some(section.id); + writer.reserve_gnu_hash_section_index(Some(section.name)) + } + SectionData::GnuVersym => { + if gnu_versym_id.is_some() { + return Err(Error("Multiple .gnu.version sections".into())); + } + gnu_versym_id = Some(section.id); + writer.reserve_gnu_versym_section_index(Some(section.name)) + } + SectionData::GnuVerdef => { + if gnu_verdef_id.is_some() { + return Err(Error("Multiple .gnu.version_d sections".into())); + } + gnu_verdef_id = Some(section.id); + writer.reserve_gnu_verdef_section_index(Some(section.name)) + } + SectionData::GnuVerneed => { + if gnu_verneed_id.is_some() { + return Err(Error("Multiple .gnu.version_r sections".into())); + } + gnu_verneed_id = Some(section.id); + writer.reserve_gnu_verneed_section_index(Some(section.name)) + } + SectionData::GnuAttributes => { + if gnu_attributes_id.is_some() { + return Err(Error("Multiple .gnu.attributes sections".into())); + } + gnu_attributes_id = Some(section.id); + writer.reserve_gnu_attributes_section_index(Some(section.name)) + } + }; + out_sections_index.push(Some(index)); + + let name = if section.name.is_empty() { + None + } else { + Some(writer.add_section_name(section.name)) + }; + out_sections.push(SectionOut { + id: section.id, + name, + }); + } + + // Assign dynamic strings. + 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); + } + } + } + } + + // Assign dynamic symbol indices. + let mut out_dynsyms = Vec::with_capacity(self.dynamic_symbols.len()); + let mut gnu_hash_symbol_count = 0; + for symbol in &self.dynamic_symbols { + if symbol.delete { + 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)); + gnu_hash_symbol_count += 1; + } + } + 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(".gnu.hash bucket count is zero".into())); + } + // 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.len()]; + for out_dynsym in &mut out_dynsyms { + out_dynsyms_index[out_dynsym.id.index] = Some(writer.reserve_dynamic_symbol_index()); + } + + // Hash parameters. + let hash_index_base = 1; // Null symbol. + let hash_chain_count = hash_index_base + out_dynsyms.len() as u32; + + // GNU hash parameters. + let gnu_hash_index_base = if gnu_hash_symbol_count == 0 { + 0 + } else { + out_dynsyms.len() as u32 - gnu_hash_symbol_count + }; + let gnu_hash_symbol_base = gnu_hash_index_base + 1; // Null symbol. + + // Assign symbol indices. + let mut out_syms = Vec::with_capacity(self.symbols.len()); + // 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; + } + + 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.v[sym.id.index].st_bind() == elf::STB_LOCAL) + .count() as u32; + let mut out_syms_index = vec![Default::default(); self.symbols.len()]; + for out_sym in out_syms.iter_mut() { + out_syms_index[out_sym.id.index] = Some(writer.reserve_symbol_index(None)); + } + + // Count the versions and add version strings. + 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.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 { + if version.delete { + continue; + } + match &version.data { + VersionData::Def(def) => { + verdef_count += 1; + verdaux_count += def.names.len(); + for name in &def.names { + writer.add_dynamic_string(name); + } + } + VersionData::Need(need) => { + vernaux_count += 1; + writer.add_dynamic_string(need.name); + out_version_files[need.file.0].versions.push(version.id); + } + } + } + for file in &self.version_files.0 { + if file.delete { + 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 { + // TODO: not sure if these dynamic or not + if !id.dynamic { + return Err(Error("Invalid symbol id in attribute".into())); + } + if let Some(index) = out_dynsyms_index[id.index] { + 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); + } + + // TODO: support section headers in strtab + if shstrtab_id.is_none() && !out_sections.is_empty() { + return Err(Error(".shstrtab section is needed but not present".into())); + } + if symtab_id.is_none() && !out_syms.is_empty() { + return Err(Error(".symtab section is needed but not present".into())); + } + if symtab_shndx_id.is_none() && writer.symtab_shndx_needed() { + return Err(Error( + ".symtab.shndx section is needed but not present".into(), + )); + } + if strtab_id.is_none() && writer.strtab_needed() { + return Err(Error(".strtab section is needed but not present".into())); + } + if dynsym_id.is_none() && !out_dynsyms.is_empty() { + return Err(Error(".dynsym section is needed but not present".into())); + } + if dynstr_id.is_none() && writer.dynstr_needed() { + return Err(Error(".dynstr section is needed but not present".into())); + } + if gnu_verdef_id.is_none() && verdef_count > 0 { + return Err(Error( + ".gnu.version_d section is needed but not present".into(), + )); + } + if gnu_verneed_id.is_none() && verneed_count > 0 { + return Err(Error( + ".gnu.version_r section is needed but not present".into(), + )); + } + if gnu_attributes_id.is_none() && !gnu_attributes.is_empty() { + return Err(Error( + ".gnu.attributes section is needed but not present".into(), + )); + } + + // Start reserving file ranges. + writer.reserve_file_header(); + + let mut dynsym_addr = 0; + let mut dynstr_addr = 0; + 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; + + if !self.segments.0.is_empty() { + // TODO: support program headers in other locations. + if self.e_phoff != writer.reserved_len() as u64 { + 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 = out_sections + .iter() + .enumerate() + .filter_map(|(index, out_section)| { + let section = &self.sections.0[out_section.id.0]; + if section.is_alloc() { + Some(index) + } else { + None + } + }) + .collect(); + // The data for alloc sections may need to be written in a different order + // from their section headers. + alloc_sections + .sort_by_key(|index| self.sections.0[out_sections[*index].id.0].sh_offset); + for index in &alloc_sections { + let out_section = &mut out_sections[*index]; + let section = &self.sections.0[out_section.id.0]; + + if section.sh_offset < writer.reserved_len() as u64 { + return Err(Error(format!( + "Unsupported section '{}' sh_offset value 0x{:x}, expected at least 0x{:x}", + String::from_utf8_lossy(section.name), + 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 as usize); + let offset = match §ion.data { + SectionData::Data(data) => { + writer.reserve(data.len(), section.sh_addralign as usize) + } + SectionData::UninitializedData(_) => { + // Note: unaligned input sh_offset was observed in practice. + writer.reserve(0, 1) + } + SectionData::Relocation(relocations) => writer + .reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA), + SectionData::Note(data) => { + writer.reserve(data.len(), section.sh_addralign as usize) + } + SectionData::Dynamic(dynamics) => writer.reserve_dynamics(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 as u64 { + return Err(Error(format!( + "Unaligned sh_offset value 0x{:x}", + section.sh_offset + ))); + } + } + } + + // Reserve non-alloc sections at any offset. + for out_section in &mut out_sections { + let section = &mut self.sections.0[out_section.id.0]; + if !self.segments.0.is_empty() && section.is_alloc() { + continue; + } + match §ion.data { + SectionData::Data(data) => { + section.sh_offset = + writer.reserve(data.len(), section.sh_addralign as usize) as u64; + } + SectionData::UninitializedData(_) => { + section.sh_offset = writer.reserve(0, section.sh_addralign as usize) as u64; + } + SectionData::Note(data) => { + section.sh_offset = + writer.reserve(data.len(), section.sh_addralign as usize) as u64; + } + SectionData::GnuAttributes => { + section.sh_offset = + writer.reserve_gnu_attributes(gnu_attributes.len() as usize) as u64; + } + // These are handled elsewhere. + SectionData::Relocation(_) + | SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String => {} + _ => { + return Err(Error(format!( + "Unsupported non-alloc section type {:x}", + section.sh_type + ))); + } + } + } + + writer.reserve_symtab(); + writer.reserve_symtab_shndx(); + writer.reserve_strtab(); + + // Reserve non-alloc relocations. + for out_section in &mut out_sections { + let section = &mut self.sections.0[out_section.id.0]; + if !self.segments.0.is_empty() && section.is_alloc() { + continue; + } + let SectionData::Relocation(relocations) = §ion.data else { + continue; + }; + section.sh_offset = writer + .reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA) + as u64; + } + + 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 index in &alloc_sections { + let out_section = &mut out_sections[*index]; + let section = &self.sections.0[out_section.id.0]; + writer.pad_until(section.sh_offset as usize); + match §ion.data { + SectionData::Data(data) => { + writer.write(data); + } + SectionData::UninitializedData(_) => {} + SectionData::Relocation(relocations) => { + for rel in relocations { + let r_sym = if let Some(symbol) = rel.symbol { + if !symbol.dynamic || section.sh_link_section != dynsym_id { + return Err(Error( + "Invalid symbol id in dynamic relocation".into(), + )); + } + out_dynsyms_index[symbol.index].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::Note(data) => { + writer.write(data); + } + SectionData::Dynamic(dynamics) => { + for d in dynamics { + match *d { + Dynamic::Auto { tag } => { + // TODO: support more values + let val = match tag { + elf::DT_NULL => 0, + elf::DT_SYMTAB => dynsym_addr, + elf::DT_STRTAB => dynstr_addr, + elf::DT_STRSZ => writer.dynstr_len() as u64, + elf::DT_HASH => hash_addr, + elf::DT_GNU_HASH => gnu_hash_addr, + elf::DT_VERSYM => versym_addr, + elf::DT_VERDEF => verdef_addr, + elf::DT_VERDEFNUM => verdef_count as u64, + elf::DT_VERNEED => verneed_addr, + elf::DT_VERNEEDNUM => verneed_count as u64, + _ => { + return Err(Error(format!( + "Cannot generate value for dynamic tag 0x{:x}", + tag + ))) + } + }; + writer.write_dynamic(tag, val); + } + Dynamic::Integer { tag, val } => { + writer.write_dynamic(tag, val); + } + Dynamic::String { tag, val } => { + writer + .write_dynamic_string(tag, writer.get_dynamic_string(val)); + } + } + } + } + SectionData::DynamicSymbol => { + writer.write_null_dynamic_symbol(); + for out_dynsym in &out_dynsyms { + let symbol = &self.dynamic_symbols.v[out_dynsym.id.index]; + 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, + }); + } + } + SectionData::DynamicString => { + writer.write_dynstr(); + } + SectionData::Hash => { + if self.hash_bucket_count == 0 { + return Err(Error(".hash bucket count is zero".into())); + } + 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(".gnu.hash bucket count is zero".into())); + } + 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.v[out_dynsym.id.index]; + 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.0 { + if version.delete { + continue; + } + if let VersionData::Def(def) = &version.data { + 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 { + version: elf::VER_DEF_CURRENT, + flags: def.flags, + index: version.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 { + let version = &self.versions.0[id.0 - 2]; + // This will always match. + if let VersionData::Need(need) = &version.data { + debug_assert_eq!(*id, version.id); + writer.write_gnu_vernaux(&write::elf::Vernaux { + flags: need.flags, + index: version.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 out_section in &mut out_sections { + let section = &self.sections.0[out_section.id.0]; + if !self.segments.0.is_empty() && section.is_alloc() { + continue; + } + match §ion.data { + SectionData::Data(data) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(section.sh_offset, writer.len() as u64); + writer.write(data); + } + SectionData::UninitializedData(_) => { + // Nothing to do. + } + SectionData::Note(data) => { + writer.write_align(section.sh_addralign as usize); + debug_assert_eq!(section.sh_offset, writer.len() as u64); + writer.write(data); + } + SectionData::GnuAttributes => { + writer.write_gnu_attributes(&gnu_attributes); + } + // These are handled elsewhere. + SectionData::Relocation(_) + | SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String => {} + _ => { + return Err(Error(format!( + "Unsupported non-alloc section type {:x}", + section.sh_type + ))); + } + } + } + + writer.write_null_symbol(); + for out_sym in &out_syms { + let symbol = &self.symbols.v[out_sym.id.index]; + 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.is_alloc() { + continue; + } + let SectionData::Relocation(relocations) = §ion.data else { + continue; + }; + let (dynamic, reloc_syms_index) = if section.sh_link_section.is_none() { + (None, &[][..]) + } else if section.sh_link_section == symtab_id { + (Some(false), &*out_syms_index) + } else if section.sh_link_section == dynsym_id { + (Some(true), &*out_dynsyms_index) + } else { + return Err(Error(format!( + "Invalid sh_link for relocation section {}", + String::from_utf8_lossy(section.name), + ))); + }; + writer.write_align_relocation(); + for rel in relocations { + let r_sym = if let Some(symbol) = rel.symbol { + if Some(symbol.dynamic) != dynamic { + return Err(Error("Invalid symbol id in relocation".into())); + } + reloc_syms_index[symbol.index].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(_) + | SectionData::Note(_) + | SectionData::Dynamic(_) => { + 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::Note(data) => data.len() as u64, + SectionData::Dynamic(dynamics) => { + (dynamics.len() * self.class().dyn_size()) as u64 + } + _ => 0, + }; + let sh_link = if let Some(id) = section.sh_link_section { + if let Some(index) = out_sections_index[id.0] { + index.0 + } else { + return Err(Error(format!( + "Invalid sh_link from section '{}' to deleted section '{}'", + String::from_utf8_lossy(section.name), + String::from_utf8_lossy(self.sections.0[id.0].name), + ))); + } + } 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 { + return Err(Error(format!( + "Invalid sh_info link from section '{}' to deleted section '{}'", + String::from_utf8_lossy(section.name), + String::from_utf8_lossy(self.sections.0[id.0].name), + ))); + } + } 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: section.sh_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::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(section.sh_addr); + } + SectionData::GnuHash => { + writer.write_gnu_hash_section_header(section.sh_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(()) + } + + /// Set the delete flag for symbols that refer to deleted sections. + pub fn delete_orphan_symbols(&mut self) { + for symbol in &mut self.symbols { + if let Some(section) = symbol.section { + if self.sections.0[section.0].delete { + symbol.delete = true; + } + } + } + for symbol in &mut self.dynamic_symbols { + if let Some(section) = symbol.section { + if self.sections.0[section.0].delete { + symbol.delete = true; + } + } + } + } + + /// Delete relocations that refer to deleted symbols. + pub fn delete_orphan_relocations(&mut self) { + let symbols = &self.symbols; + let dynamic_symbols = &self.dynamic_symbols; + for section in &mut self.sections.0 { + let SectionData::Relocation(relocations) = &mut section.data else { + continue; + }; + relocations.retain(|relocation| match relocation.symbol { + None => true, + Some(symbol) => { + if symbol.dynamic { + !dynamic_symbols.v[symbol.index].delete + } else { + !symbols.v[symbol.index].delete + } + } + }); + } + } + + /// Delete unused GNU version entries. + pub fn delete_unused_versions(&mut self) { + let mut version_used = vec![false; self.versions.len() + VERSION_ID_BASE]; + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + version_used[symbol.version.0] = true; + } + let mut version_file_used = vec![false; self.version_files.len()]; + for version in &mut self.versions.0 { + if !version_used[version.id().0] { + version.delete = true; + } + if version.delete { + continue; + } + if let VersionData::Need(need) = &version.data { + version_file_used[need.file.0] = true; + } + } + for file in &mut self.version_files.0 { + if !version_file_used[file.id.0] { + file.delete = true; + } + } + } + + /// Return the ELF file class that will be written. + /// + /// This can be useful for calculating sizes. + pub fn class(&self) -> write::elf::Class { + write::elf::Class { is_64: self.is_64 } + } + + /// Calculate the size of the file header. + pub fn file_header_size(&self) -> usize { + self.class().file_header_size() + } + + /// Calculate the size of the program headers. + pub fn program_headers_size(&self) -> usize { + let count = self + .segments + .iter() + .filter(|segment| !segment.delete) + .count(); + count * self.class().program_header_size() + } + + /// Calculate the size of the dynamic symbol table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn dynamic_symbol_size(&self) -> usize { + let mut count = 1; + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + count += 1; + } + count * self.class().sym_size() + } + + /// Calculate the size of the dynamic string table. + /// + /// This adds all of the currently used dynamic strings to a string table, + /// calculates the size of the string table, and discards the string table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn dynamic_string_size(&self) -> usize { + let mut dynstr = write::string::StringTable::default(); + for section in &self.sections { + if section.delete { + continue; + } + if let SectionData::Dynamic(dynamics) = §ion.data { + for dynamic in dynamics { + if let Dynamic::String { val, .. } = dynamic { + dynstr.add(val); + } + } + } + } + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + dynstr.add(symbol.name); + } + if let Some(version_base) = self.version_base { + dynstr.add(version_base); + } + for version in &self.versions { + if version.delete { + continue; + } + match &version.data { + VersionData::Def(def) => { + for name in &def.names { + dynstr.add(name); + } + } + VersionData::Need(need) => { + dynstr.add(need.name); + } + } + } + for file in &self.version_files { + if file.delete { + continue; + } + dynstr.add(file.name); + } + 1 + dynstr.size() + } + + /// Calculate the size of the hash table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn hash_size(&self) -> usize { + let mut chain_count = 1; // Null symbol. + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + chain_count += 1; + } + self.class().hash_size(self.hash_bucket_count, chain_count) + } + + /// Calculate the size of the GNU hash table. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`]. + pub fn gnu_hash_size(&self) -> usize { + let mut symbol_count = 0; + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + if symbol.st_shndx == elf::SHN_UNDEF { + continue; + } + symbol_count += 1; + } + self.class().gnu_hash_size( + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + symbol_count, + ) + } + + /// Calculate the size of the GNU symbol version section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_versym_size(&self) -> usize { + let mut symbol_count = 1; // Null symbol. + for symbol in &self.dynamic_symbols { + if symbol.delete { + continue; + } + symbol_count += 1; + } + self.class().gnu_versym_size(symbol_count) + } + + /// Calculate the size of the GNU version definition section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_verdef_size(&self) -> usize { + let mut verdef_count = 0; + let mut verdaux_count = 0; + if self.version_base.is_some() { + verdef_count += 1; + verdaux_count += 1; + } + for version in &self.versions.0 { + if version.delete { + continue; + } + if let VersionData::Def(def) = &version.data { + verdef_count += 1; + verdaux_count += def.names.len(); + } + } + self.class().gnu_verdef_size(verdef_count, verdaux_count) + } + + /// Calculate the size of the GNU version dependency section. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn gnu_verneed_size(&self) -> usize { + let mut verneed_count = 0; + let mut vernaux_count = 0; + for version in &self.versions.0 { + if version.delete { + continue; + } + if let VersionData::Need(_) = &version.data { + vernaux_count += 1; + } + } + for file in &self.version_files.0 { + if file.delete { + continue; + } + verneed_count += 1; + } + self.class().gnu_verneed_size(verneed_count, vernaux_count) + } + + /// Calculate the memory size of a section. + /// + /// Returns 0 for sections that are deleted or aren't allocated. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn section_size(&self, section: &Section<'_>) -> usize { + if section.delete || !section.is_alloc() { + return 0; + } + match §ion.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) + } + SectionData::Note(data) => data.len(), + SectionData::Dynamic(dynamics) => dynamics.len() * self.class().dyn_size(), + SectionData::DynamicString => self.dynamic_string_size(), + SectionData::DynamicSymbol => self.dynamic_symbol_size(), + SectionData::Hash => self.hash_size(), + SectionData::GnuHash => self.gnu_hash_size(), + SectionData::GnuVersym => self.gnu_versym_size(), + SectionData::GnuVerdef => self.gnu_verdef_size(), + SectionData::GnuVerneed => self.gnu_verneed_size(), + SectionData::SectionString + | SectionData::Symbol + | SectionData::SymbolSectionIndex + | SectionData::String + | SectionData::GnuAttributes => 0, + } + } + + /// Set the `sh_size` field for every allocated section. + /// + /// This is useful to call prior to doing memory layout. + /// + /// To get an accurate result, you may need to first call + /// [`Self::delete_orphan_symbols`] and [`Self::delete_unused_versions`]. + pub fn set_section_sizes(&mut self) { + for i in 0..self.sections.0.len() { + let section = &self.sections.0[i]; + if section.delete || !section.is_alloc() { + continue; + } + self.sections.0[i].sh_size = self.section_size(section) as u64; + } + } +} + +/// An ID for referring to a segment in [`Segments`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SegmentId(usize); + +/// A segment in [`Segments`]. +/// +/// This corresponds to [`elf::ProgramHeader32`] or [`elf::ProgramHeader64`]. +#[derive(Debug)] +pub struct Segment<'data> { + id: SegmentId, + /// The `p_type` field in the ELF program header. + /// + /// One of the `PT_*` constants. + pub p_type: u32, + /// The `p_flags` field in the ELF program header. + /// + /// A combination of the `PF_*` constants. + pub p_flags: u32, + /// The `p_offset` field in the ELF program header. + /// + /// This is the file offset of the data in the segment. This should + /// correspond to the file offset of the sections that are placed in + /// this segment. Currently there is no support for section data + /// that is not contained in sections. + pub p_offset: u64, + /// The `p_vaddr` field in the ELF program header. + pub p_vaddr: u64, + /// The `p_paddr` field in the ELF program header. + pub p_paddr: u64, + /// The `p_filesz` field in the ELF program header. + pub p_filesz: u64, + /// The `p_memsz` field in the ELF program header. + pub p_memsz: u64, + /// The `p_align` field in the ELF program header. + pub p_align: u64, + /// Ignore this segment when writing the ELF file. + pub delete: bool, + /// The sections contained in this segment. + pub sections: Vec, + // Might need to add reference to data if no sections. + marker: PhantomData<&'data ()>, +} + +impl<'data> Segment<'data> { + /// The ID used for referring to this segment. + pub fn id(&self) -> SegmentId { + self.id + } + + /// Returns true if the segment type is `PT_LOAD`. + pub fn is_load(&self) -> bool { + self.p_type == elf::PT_LOAD + } + + /// Returns true if the segment contains the given file offset. + pub fn contains_offset(&self, offset: u64) -> bool { + offset >= self.p_offset && offset - self.p_offset < self.p_filesz + } + + /// Return the address corresponding to the given file offset. + /// + /// This will return a meaningless value if `contains_offset` is false. + pub fn address_from_offset(&self, offset: u64) -> u64 { + self.p_vaddr + .wrapping_add(offset.wrapping_sub(self.p_offset)) + } + + /// Returns true if the segment contains the given address. + pub fn contains_address(&self, address: u64) -> bool { + address >= self.p_vaddr && address - self.p_vaddr < self.p_memsz + } + + /// Remove all sections from the segment, and set its size to zero. + pub fn remove_sections(&mut self) { + self.p_filesz = 0; + self.p_memsz = 0; + self.sections.clear(); + } + + /// Extend this segment's file and address ranges to include the given section. + /// + /// This uses the `sh_size` field of the section. + /// + /// If this is a [`elf::PT_LOAD`] segment, then the file offset and address of the + /// section is changed to be at the end of the segment. + pub fn append_section(&mut self, section: &mut Section<'_>) { + debug_assert_eq!(self.p_filesz, self.p_memsz); + if self.p_type == elf::PT_LOAD { + let align = section.sh_addralign; + let offset = (self.p_offset + self.p_filesz + (align - 1)) & !(align - 1); + let addr = (self.p_paddr + self.p_memsz + (align - 1)) & !(align - 1); + section.sh_offset = offset; + section.sh_addr = addr; + } + if self.p_memsz == 0 { + self.p_offset = section.sh_offset; + self.p_filesz = section.sh_size; + self.p_vaddr = section.sh_addr; + self.p_paddr = section.sh_addr; + self.p_memsz = section.sh_size; + } else { + if self.p_offset > section.sh_offset { + self.p_offset = section.sh_offset; + } + let filesz = section.sh_offset + section.sh_size - self.p_offset; + if self.p_filesz < filesz { + self.p_filesz = filesz; + } + if self.p_vaddr > section.sh_addr { + self.p_vaddr = section.sh_addr; + self.p_paddr = section.sh_addr; + } + let memsz = section.sh_addr + section.sh_size - self.p_vaddr; + if self.p_memsz < memsz { + self.p_memsz = memsz; + } + } + self.sections.push(section.id()); + } +} + +/// A segment table. +#[derive(Debug)] +pub struct Segments<'data>(Vec>); + +impl<'data> Segments<'data> { + fn new() -> Self { + Segments(Vec::new()) + } + + /// Return `True` if there are no segments. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Number of segments. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Return a reference to a segment. + pub fn get(&self, id: SegmentId) -> &Segment<'data> { + self.0.get(id.0).unwrap() + } + + /// Return a mutable reference to a segment. + pub fn get_mut(&mut self, id: SegmentId) -> &mut Segment<'data> { + self.0.get_mut(id.0).unwrap() + } + + /// Return an iterator for the segments. + pub fn iter(&self) -> core::slice::Iter<'_, Segment<'data>> { + self.into_iter() + } + + /// Return a mutable iterator for the segments. + pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Segment<'data>> { + self.into_iter() + } + + /// Add a new segment to the table. + pub fn add(&mut self) -> &mut Segment<'data> { + let id = SegmentId(self.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, + sections: Vec::new(), + delete: false, + marker: PhantomData, + }); + self.get_mut(id) + } + + /// Find a `PT_LOAD` segment containing the given offset. + pub fn find_load_segment_from_offset(&self, offset: u64) -> Option<&Segment<'data>> { + // FIXME: should this ignore deleted segments? + self.iter() + .find(|segment| segment.is_load() && segment.contains_offset(offset)) + } + + /// Add a new `PT_LOAD` segment to the table. + /// + /// The file offset and address will be set to the aligned value of the current maximum for any segment. + pub fn add_load_segment(&mut self, flags: u32, align: u64) -> &mut Segment<'data> { + let mut max_offset = 0; + let mut max_addr = 0; + for segment in &*self { + let offset = segment.p_offset + segment.p_filesz; + if max_offset < offset { + max_offset = offset; + } + let addr = segment.p_vaddr + segment.p_memsz; + if max_addr < addr { + max_addr = addr; + } + } + // FIXME: is alignment needed? + let offset = (max_offset + (align - 1)) & !(align - 1); + let addr = (max_addr + (align - 1)) & !(align - 1); + let segment = self.add(); + segment.p_type = elf::PT_LOAD; + segment.p_flags = flags; + segment.p_offset = offset; + segment.p_vaddr = addr; + segment.p_paddr = addr; + segment.p_align = align; + segment + } + + /// Add a copy of a segment to the table. + /// + /// This will copy the segment type, flags and alignment. + /// + /// Additionally, if the segment type is `PT_LOAD`, then the file offset and address + /// will be set as in `add_load_segment`. + pub fn copy(&mut self, id: SegmentId) -> &mut Segment<'data> { + let segment = self.get(id); + let p_type = segment.p_type; + let p_flags = segment.p_flags; + let p_align = segment.p_align; + if p_type == elf::PT_LOAD { + self.add_load_segment(p_flags, p_align) + } else { + let segment = self.add(); + segment.p_type = p_type; + segment.p_flags = p_flags; + segment.p_align = p_align; + segment + } + } +} + +impl<'a, 'data> IntoIterator for &'a Segments<'data> { + type Item = &'a Segment<'data>; + type IntoIter = core::slice::Iter<'a, Segment<'data>>; + fn into_iter(self) -> core::slice::Iter<'a, Segment<'data>> { + self.0.iter() + } +} + +impl<'a, 'data> IntoIterator for &'a mut Segments<'data> { + type Item = &'a mut Segment<'data>; + type IntoIter = core::slice::IterMut<'a, Segment<'data>>; + fn into_iter(self) -> core::slice::IterMut<'a, Segment<'data>> { + self.0.iter_mut() + } +} + +/// An ID for referring to a section in [`Sections`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SectionId(usize); + +/// A section in [`Sections`]. +/// +/// This corresponds to [`elf::SectionHeader32`] or [`elf::SectionHeader64`]. +#[derive(Debug)] +pub struct Section<'data> { + id: SectionId, + /// The name of the section. + /// + /// This is automatically added to the section header string table, + /// and the resulting string table offset is used to set the `sh_name` + /// field in the ELF section header. + pub name: &'data [u8], + /// The `sh_type` field in the ELF section header. + /// + /// One of the `SHT_*` constants. + pub sh_type: u32, + /// The `sh_flags` field in the ELF section header. + /// + /// A combination of the `SHF_*` constants. + pub sh_flags: u64, + /// The `sh_addr` field in the ELF section header. + pub sh_addr: u64, + /// The `sh_offset` field in the ELF section header. + /// + /// This is the file offset of the data in the section. + /// Writing will fail if the data cannot be placed at this offset. + /// + /// This is only used for sections that have `SHF_ALLOC` set. + /// For other sections, the section data is written at the next available + /// offset. + pub sh_offset: u64, + /// The `sh_size` field in the ELF section header. + /// + /// This size is not used when writing. The size of the `data` field is + /// used instead. + pub sh_size: u64, + /// The ID of the section linked to by the `sh_link` field in the ELF section header. + pub sh_link_section: Option, + /// The `sh_info` field in the ELF section header. + /// + /// Only used if `sh_info_section` is `None`. + pub sh_info: u32, + /// The ID of the section linked to by the `sh_info` field in the ELF section header. + pub sh_info_section: Option, + /// The `sh_addralign` field in the ELF section header. + pub sh_addralign: u64, + /// The `sh_entsize` field in the ELF section header. + pub sh_entsize: u64, + /// The section data. + pub data: SectionData<'data>, + /// Ignore this section when writing the ELF file. + pub delete: bool, +} + +impl<'data> Section<'data> { + /// The ID used for referring to this section. + pub fn id(&self) -> SectionId { + self.id + } + + /// Returns true if the section flags include `SHF_ALLOC`. + pub fn is_alloc(&self) -> bool { + self.sh_flags & u64::from(elf::SHF_ALLOC) != 0 + } + + /// Return the segment permission flags that are equivalent to the section flags. + pub fn p_flags(&self) -> u32 { + let mut p_flags = elf::PF_R; + if self.sh_flags & u64::from(elf::SHF_WRITE) != 0 { + p_flags |= elf::PF_W; + } + if self.sh_flags & u64::from(elf::SHF_EXECINSTR) != 0 { + p_flags |= elf::PF_X; + } + p_flags + } +} + +/// The data for a [`Section`]. +#[derive(Debug, Clone)] +pub enum SectionData<'data> { + /// The section contains the given raw data bytes. + Data(&'data [u8]), + /// The section contains uninitialised data bytes of the given length. + UninitializedData(u64), + /// The section contains relocations. + Relocation(Vec), + /// The section contains notes. + // TODO: parse notes + Note(&'data [u8]), + /// The section contains dynamic entries. + Dynamic(Vec>), + /// The section contains the strings for the section headers. + SectionString, + /// The section contains the symbol table. + Symbol, + /// The section contains the extended section index for the symbol table. + SymbolSectionIndex, + /// The section contains the strings for symbol table. + String, + /// The section contains the dynamic symbol table. + DynamicSymbol, + /// The section contains the dynamic string table. + DynamicString, + /// The section contains the hash table. + Hash, + /// The section contains the GNU hash table. + GnuHash, + /// The section contains the GNU symbol versions. + GnuVersym, + /// The section contains the GNU version definitions. + GnuVerdef, + /// The section contains the GNU version dependencies. + GnuVerneed, + /// The section contains the GNU attributes. + GnuAttributes, +} + +/// A section table. +#[derive(Debug)] +pub struct Sections<'data>(Vec>); + +impl<'data> Sections<'data> { + fn new() -> Self { + Sections(Vec::new()) + } + + /// Return `True` if there are no sections. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Number of sections. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Add a new section to the table. + pub fn add(&mut self) -> &mut Section<'data> { + let id = SectionId(self.len()); + self.0.push(Section { + id, + name: &[], + sh_type: 0, + sh_flags: 0, + sh_addr: 0, + sh_offset: 0, + sh_size: 0, + sh_link_section: None, + sh_info: 0, + sh_info_section: None, + sh_addralign: 0, + sh_entsize: 0, + data: SectionData::Data(&[]), + delete: false, + }); + self.0.last_mut().unwrap() + } + + /// Add a copy of a section to the table. + /// + /// This will set the file offset of the copy to zero. + /// [`Segment::append_section`] can be used to assign a valid file offset and a new address. + pub fn copy(&mut self, id: SectionId) -> &mut Section<'data> { + let section = self.get(id); + let id = SectionId(self.len()); + let name = section.name; + let sh_type = section.sh_type; + let sh_flags = section.sh_flags; + let sh_addr = section.sh_addr; + let sh_size = section.sh_size; + let sh_link_section = section.sh_link_section; + let sh_info = section.sh_info; + let sh_info_section = section.sh_info_section; + let sh_addralign = section.sh_addralign; + let sh_entsize = section.sh_entsize; + let data = section.data.clone(); + self.0.push(Section { + id, + name, + sh_type, + sh_flags, + sh_addr, + sh_offset: 0, + sh_size, + sh_link_section, + sh_info, + sh_info_section, + sh_addralign, + sh_entsize, + data, + delete: false, + }); + self.0.last_mut().unwrap() + } + + /// Return an iterator for the sections. + pub fn iter(&self) -> core::slice::Iter<'_, Section<'data>> { + self.into_iter() + } + + /// Return a mutable iterator for the sections. + pub fn iter_mut(&mut self) -> core::slice::IterMut<'_, Section<'data>> { + self.into_iter() + } + + /// Return a reference to a section. + pub fn get(&self, id: SectionId) -> &Section<'data> { + self.0.get(id.0).unwrap() + } + + /// Return a mutable reference to a section. + pub fn get_mut(&mut self, id: SectionId) -> &mut Section<'data> { + self.0.get_mut(id.0).unwrap() + } +} + +impl<'a, 'data> IntoIterator for &'a Sections<'data> { + type Item = &'a Section<'data>; + type IntoIter = core::slice::Iter<'a, Section<'data>>; + fn into_iter(self) -> core::slice::Iter<'a, Section<'data>> { + self.0.iter() + } +} + +impl<'a, 'data> IntoIterator for &'a mut Sections<'data> { + type Item = &'a mut Section<'data>; + type IntoIter = core::slice::IterMut<'a, Section<'data>>; + fn into_iter(self) -> core::slice::IterMut<'a, Section<'data>> { + self.0.iter_mut() + } +} + +/// An ID for referring to a symbol in [`Symbols`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SymbolId { + index: usize, + dynamic: bool, +} + +/// A symbol in [`Symbols`]. +/// +/// This corresponds to [`elf::Sym32`] or [`elf::Sym64`]. +#[derive(Debug)] +pub struct Symbol<'data> { + id: SymbolId, + /// The name of the symbol. + pub name: &'data [u8], + /// The section referenced by the symbol. + /// + /// Used to set the `st_shndx` field in the ELF symbol. + pub section: Option, + /// The `st_info` field in the ELF symbol. + pub st_info: u8, + /// The `st_other` field in the ELF symbol. + pub st_other: u8, + /// The `st_shndx` field in the ELF symbol. + /// + /// Only used if `Self::section` is `None`. + pub st_shndx: u16, + /// The `st_value` field in the ELF symbol. + pub st_value: u64, + /// The `st_size` field in the ELF symbol. + pub st_size: u64, + /// GNU version for dynamic symbols. + pub version: VersionId, + /// Set the [`elf::VERSYM_HIDDEN`] flag for this symbol. + pub version_hidden: bool, + /// Ignore this symbol when writing the ELF file. + pub delete: bool, +} + +impl<'data> Symbol<'data> { + /// The ID used for referring to this symbol. + pub fn id(&self) -> SymbolId { + self.id + } + + /// 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); + } +} + +/// A symbol table. +#[derive(Debug)] +pub struct Symbols<'data> { + v: Vec>, + dynamic: bool, +} + +impl<'data> Symbols<'data> { + fn new(dynamic: bool) -> Self { + Symbols { + v: Vec::new(), + dynamic, + } + } + + /// Return `True` if there are no symbols. + pub fn is_empty(&self) -> bool { + self.v.is_empty() + } + + /// Number of symbols. + pub fn len(&self) -> usize { + self.v.len() + } + + /// Add a new symbol to the table. + pub fn add(&mut self) -> &mut Symbol<'data> { + let id = SymbolId { + index: self.len(), + dynamic: self.dynamic, + }; + self.v.push(Symbol { + id, + name: &[], + section: None, + st_info: 0, + st_other: 0, + st_shndx: 0, + st_value: 0, + st_size: 0, + version: VersionId::local(), + version_hidden: false, + delete: false, + }); + self.v.last_mut().unwrap() + } + + /// Return a reference to a symbol. + pub fn get(&self, id: SymbolId) -> Option<&Symbol<'data>> { + if id.dynamic != self.dynamic { + None + } else { + self.v.get(id.index) + } + } + + /// Return a mutable reference to a symbol. + pub fn get_mut(&mut self, id: SymbolId) -> Option<&mut Symbol<'data>> { + if id.dynamic != self.dynamic { + None + } else { + self.v.get_mut(id.index) + } + } +} + +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.v.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.v.iter_mut() + } +} + +/// A relocation stored in a [`Section`]. +/// +/// This corresponds to [`elf::Rel32`], [`elf::Rela32`], [`elf::Rel64`] or [`elf::Rela64`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Relocation { + /// The `r_offset` field in the ELF relocation. + pub r_offset: u64, + /// The symbol referenced by the ELF relocation. + pub symbol: Option, + /// The `r_type` field in the ELF relocation. + pub r_type: u32, + /// The `r_addend` field in the ELF relocation. + /// + /// Only used if the section type is `SHT_RELA`. + pub r_addend: i64, +} + +/// An entry in the dynamic section. +/// +/// This corresponds to [`elf::Dyn32`] or [`elf::Dyn64`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Dynamic<'data> { + /// The value is an automatically generated integer. + /// + /// Writing will fail if the value cannot be automatically generated. + Auto { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + }, + /// The value is an integer. + Integer { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + /// The `d_val` field in the dynamic entry. + val: u64, + }, + /// The value is a string. + String { + /// The `d_tag` field in the dynamic entry. + /// + /// One of the `DT_*` values. + tag: u32, + /// The string value. + /// + /// This will be stored in the dynamic string section. + val: &'data [u8], + }, +} + +/// An ID for referring to a filename used for GNU versioning. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VersionFileId(usize); + +/// A filename used for GNU versioning. +/// +/// Stored in [`VersionFiles`]. +#[derive(Debug)] +pub struct VersionFile<'data> { + id: VersionFileId, + /// The filename. + pub name: &'data [u8], + /// Ignore this file when writing the ELF file. + pub delete: bool, +} + +impl<'data> VersionFile<'data> { + /// The ID used for referring to this filename. + pub fn id(&self) -> VersionFileId { + self.id + } +} + +/// A table of filenames used for GNU versioning. +#[derive(Debug)] +pub struct VersionFiles<'data>(Vec>); + +impl<'data> VersionFiles<'data> { + fn new() -> Self { + VersionFiles(Vec::new()) + } + + /// Return `True` if there are no filenames. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Number of filenames. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Add a new filename to the table. + pub fn add(&mut self, name: &'data [u8]) -> VersionFileId { + let id = VersionFileId(self.len()); + self.0.push(VersionFile { + id, + name, + delete: false, + }); + id + } +} + +impl<'a, 'data> IntoIterator for &'a VersionFiles<'data> { + type Item = &'a VersionFile<'data>; + type IntoIter = core::slice::Iter<'a, VersionFile<'data>>; + fn into_iter(self) -> core::slice::Iter<'a, VersionFile<'data>> { + self.0.iter() + } +} + +impl<'a, 'data> IntoIterator for &'a mut VersionFiles<'data> { + type Item = &'a mut VersionFile<'data>; + type IntoIter = core::slice::IterMut<'a, VersionFile<'data>>; + fn into_iter(self) -> core::slice::IterMut<'a, VersionFile<'data>> { + self.0.iter_mut() + } +} + +const VERSION_ID_BASE: usize = 2; + +/// An ID for referring to a version in [`Versions`]. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct VersionId(usize); + +impl VersionId { + /// Return the ID for a version index of [`elf::VER_NDX_LOCAL`]. + pub fn local() -> Self { + VersionId(elf::VER_NDX_LOCAL as usize) + } + + /// Return the ID for a version index of [`elf::VER_NDX_GLOBAL`]. + pub fn global() -> Self { + VersionId(elf::VER_NDX_GLOBAL as usize) + } +} + +/// A version for a symbol. +#[derive(Debug)] +pub struct Version<'data> { + id: VersionId, + /// The data for this version. + pub data: VersionData<'data>, + /// Ignore this version when writing the ELF file. + pub delete: bool, +} + +impl<'data> Version<'data> { + /// The ID used for referring to this version. + pub fn id(&self) -> VersionId { + self.id + } +} + +/// The data for a version for a symbol. +#[derive(Debug)] +pub enum VersionData<'data> { + /// The version for a defined symbol. + Def(VersionDef<'data>), + /// The version for an undefined symbol. + Need(VersionNeed<'data>), +} + +/// A GNU version definition. +#[derive(Debug)] +pub struct VersionDef<'data> { + /// The names for the version. + /// + /// This usually has two elements. The first element is the name of this + /// version, and the second element is the name of the previous version + /// in the tree of versions. + pub names: Vec<&'data [u8]>, + /// The version flags. + /// + /// A combination of the `VER_FLG_*` constants. + pub flags: u16, +} + +/// A GNU version dependency. +#[derive(Debug)] +pub struct VersionNeed<'data> { + /// The filename of the library providing this version. + pub file: VersionFileId, + /// The name of the version. + pub name: &'data [u8], + /// The version flags. + /// + /// A combination of the `VER_FLG_*` constants. + pub flags: u16, +} + +/// A table of versions that are referenced by symbols. +#[derive(Debug)] +pub struct Versions<'data>(Vec>); + +impl<'data> Versions<'data> { + fn new() -> Self { + Versions(Vec::new()) + } + + /// Return `True` if there are no versions. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Number of versions. + pub fn len(&self) -> usize { + self.0.len() + } + + /// Add a version. + pub fn add(&mut self, data: VersionData<'data>) -> VersionId { + let id = VersionId(self.len() + VERSION_ID_BASE); + self.0.push(Version { + id, + data, + delete: false, + }); + id + } +} + +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.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.iter_mut() + } +} + +/// The contents of a GNU attributes section. +#[derive(Debug, Default)] +pub struct AttributesSection<'data> { + /// The subsections. + pub subsections: Vec>, +} + +impl<'data> AttributesSection<'data> { + /// Create a new GNU attribute section. + pub fn new() -> Self { + Self::default() + } +} + +/// A subsection of a GNU attributes section. +#[derive(Debug)] +pub struct AttributesSubsection<'data> { + /// The vendor namespace for these attributes. + pub vendor: &'data [u8], + /// The sub-subsections. + pub subsubsections: Vec>, +} + +impl<'data> AttributesSubsection<'data> { + /// Create a new subsection. + pub fn new(vendor: &'data [u8]) -> Self { + AttributesSubsection { + vendor, + subsubsections: Vec::new(), + } + } +} + +/// A sub-subsection in a GNU attributes section. +#[derive(Debug)] +pub struct AttributesSubsubsection<'data> { + /// The sub-subsection tag. + pub tag: AttributeTag, + /// The data containing the attributes. + pub data: &'data [u8], +} + +/// The tag for a sub-subsection in a GNU attributes section. +#[derive(Debug)] +pub enum AttributeTag { + /// The attributes apply to the whole file. + /// + /// Correspeonds to [`elf::Tag_File`]. + File, + /// The attributes apply to the given sections. + /// + /// Correspeonds to [`elf::Tag_Section`]. + Section(Vec), + /// The attributes apply to the given symbols. + /// + /// Correspeonds to [`elf::Tag_Symbol`]. + Symbol(Vec), +} + +impl AttributeTag { + /// Return the corresponding `elf::Tag_*` value for this tag. + 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/copy/mod.rs b/src/copy/mod.rs new file mode 100644 index 00000000..f0320b74 --- /dev/null +++ b/src/copy/mod.rs @@ -0,0 +1,40 @@ +//! Interface for copying object files. + +use alloc::string::String; +use core::{fmt, result}; +#[cfg(feature = "std")] +use std::error; + +use crate::{read, write}; + +#[cfg(feature = "elf")] +pub mod elf; + +/// The error type used within the copy module. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Error(pub(crate) String); + +impl fmt::Display for Error { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(&self.0) + } +} + +#[cfg(feature = "std")] +impl error::Error for Error {} + +impl From for Error { + fn from(error: read::Error) -> Error { + Error(format!("{}", error)) + } +} + +impl From for Error { + fn from(error: write::Error) -> Error { + Error(error.0) + } +} + +/// The result type used within the copy module. +pub type Result = result::Result; diff --git a/src/lib.rs b/src/lib.rs index 5956e06d..8f60875f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,9 @@ pub use read::*; #[cfg(feature = "write_core")] pub mod write; +#[cfg(feature = "copy_core")] +pub mod copy; + #[cfg(feature = "archive")] pub mod archive; #[cfg(feature = "elf")] diff --git a/src/read/mod.rs b/src/read/mod.rs index bc9de553..b2a9fe4a 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/object.rs b/src/write/elf/object.rs index 726785bf..0fdb1ea7 100644 --- a/src/write/elf/object.rs +++ b/src/write/elf/object.rs @@ -585,13 +585,13 @@ impl<'a> Object<'a> { } // Calculate size of symbols. - writer.reserve_symtab_section_index(); + writer.reserve_symtab_section_index(None); writer.reserve_symtab(); if writer.symtab_shndx_needed() { - writer.reserve_symtab_shndx_section_index(); + writer.reserve_symtab_shndx_section_index(None); } writer.reserve_symtab_shndx(); - writer.reserve_strtab_section_index(); + writer.reserve_strtab_section_index(None); writer.reserve_strtab(); // Calculate size of relocations. @@ -603,7 +603,7 @@ impl<'a> Object<'a> { } // Calculate size of section headers. - writer.reserve_shstrtab_section_index(); + writer.reserve_shstrtab_section_index(None); writer.reserve_shstrtab(); writer.reserve_section_headers(); diff --git a/src/write/elf/writer.rs b/src/write/elf/writer.rs index 97509249..32953344 100644 --- a/src/write/elf/writer.rs +++ b/src/write/elf/writer.rs @@ -86,10 +86,6 @@ pub struct Writer<'a> { dynsym_offset: usize, dynsym_num: u32, - dynamic_str_id: Option, - dynamic_offset: usize, - dynamic_num: usize, - hash_str_id: Option, hash_offset: usize, hash_size: usize, @@ -175,10 +171,6 @@ impl<'a> Writer<'a> { dynsym_offset: 0, dynsym_num: 0, - dynamic_str_id: None, - dynamic_offset: 0, - dynamic_num: 0, - hash_str_id: None, hash_offset: 0, hash_size: 0, @@ -210,6 +202,11 @@ impl<'a> Writer<'a> { } } + /// 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 reserved. pub fn reserved_len(&self) -> usize { self.len @@ -259,20 +256,12 @@ impl<'a> Writer<'a> { self.buffer.resize(offset); } - fn file_header_size(&self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - /// Reserve the range for the file header. /// /// This must be at the start of the file. pub fn reserve_file_header(&mut self) { debug_assert_eq!(self.len, 0); - self.reserve(self.file_header_size(), 1); + self.reserve(self.class().file_header_size(), 1); } /// Write the file header. @@ -310,13 +299,13 @@ impl<'a> Writer<'a> { padding: [0; 7], }; - let e_ehsize = self.file_header_size() as u16; + let e_ehsize = self.class().file_header_size() as u16; let e_phoff = self.segment_offset as u64; let e_phentsize = if self.segment_num == 0 { 0 } else { - self.program_header_size() as u16 + self.class().program_header_size() as u16 }; // TODO: overflow let e_phnum = self.segment_num as u16; @@ -325,7 +314,7 @@ impl<'a> Writer<'a> { let e_shentsize = if self.section_num == 0 { 0 } else { - self.section_header_size() as u16 + self.class().section_header_size() as u16 }; let e_shnum = if self.section_num >= elf::SHN_LORESERVE.into() { 0 @@ -380,14 +369,6 @@ impl<'a> Writer<'a> { Ok(()) } - fn program_header_size(&self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - /// Reserve the range for the program headers. pub fn reserve_program_headers(&mut self, num: u32) { debug_assert_eq!(self.segment_offset, 0); @@ -395,8 +376,10 @@ impl<'a> Writer<'a> { return; } self.segment_num = num; - self.segment_offset = - self.reserve(num as usize * self.program_header_size(), self.elf_align); + self.segment_offset = self.reserve( + num as usize * self.class().program_header_size(), + self.elf_align, + ); } /// Write alignment padding bytes prior to the program headers. @@ -467,14 +450,6 @@ impl<'a> Writer<'a> { SectionIndex(index) } - fn section_header_size(&self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - /// Reserve the range for the section headers. /// /// This function does nothing if no sections were reserved. @@ -486,7 +461,7 @@ impl<'a> Writer<'a> { return; } self.section_offset = self.reserve( - self.section_num as usize * self.section_header_size(), + self.section_num as usize * self.class().section_header_size(), self.elf_align, ); } @@ -606,9 +581,9 @@ impl<'a> Writer<'a> { /// /// This must be called before [`Self::reserve_shstrtab`] /// and [`Self::reserve_section_headers`]. - pub fn reserve_shstrtab_section_index(&mut self) -> SectionIndex { + pub fn reserve_shstrtab_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert_eq!(self.shstrtab_index, SectionIndex(0)); - self.shstrtab_str_id = Some(self.add_section_name(&b".shstrtab"[..])); + self.shstrtab_str_id = Some(self.add_section_name(name.unwrap_or(&b".shstrtab"[..]))); self.shstrtab_index = self.reserve_section_index(); self.shstrtab_index } @@ -681,9 +656,9 @@ impl<'a> Writer<'a> { /// Reserve the section index for the string table. /// /// This must be called before [`Self::reserve_section_headers`]. - pub fn reserve_strtab_section_index(&mut self) -> SectionIndex { + pub fn reserve_strtab_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert_eq!(self.strtab_index, SectionIndex(0)); - self.strtab_str_id = Some(self.add_section_name(&b".strtab"[..])); + self.strtab_str_id = Some(self.add_section_name(name.unwrap_or(&b".strtab"[..]))); self.strtab_index = self.reserve_section_index(); self.strtab_index } @@ -763,14 +738,6 @@ impl<'a> Writer<'a> { self.symtab_num } - fn symbol_size(&self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - /// Reserve the range for the symbol table. /// /// This range is used for a section named `.symtab`. @@ -782,7 +749,7 @@ impl<'a> Writer<'a> { return; } self.symtab_offset = self.reserve( - self.symtab_num as usize * self.symbol_size(), + self.symtab_num as usize * self.class().sym_size(), self.elf_align, ); } @@ -858,9 +825,9 @@ impl<'a> Writer<'a> { /// Reserve the section index for the symbol table. /// /// This must be called before [`Self::reserve_section_headers`]. - pub fn reserve_symtab_section_index(&mut self) -> SectionIndex { + pub fn reserve_symtab_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert_eq!(self.symtab_index, SectionIndex(0)); - self.symtab_str_id = Some(self.add_section_name(&b".symtab"[..])); + self.symtab_str_id = Some(self.add_section_name(name.unwrap_or(&b".symtab"[..]))); self.symtab_index = self.reserve_section_index(); self.symtab_index } @@ -883,11 +850,11 @@ impl<'a> Writer<'a> { sh_flags: 0, sh_addr: 0, sh_offset: self.symtab_offset as u64, - sh_size: self.symtab_num as u64 * self.symbol_size() as u64, + 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.symbol_size() as u64, + sh_entsize: self.class().sym_size() as u64, }); } @@ -930,9 +897,10 @@ impl<'a> Writer<'a> { /// unless you have other means of knowing if this section is needed. /// /// This must be called before [`Self::reserve_section_headers`]. - pub fn reserve_symtab_shndx_section_index(&mut self) -> SectionIndex { + pub fn reserve_symtab_shndx_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.symtab_shndx_str_id.is_none()); - self.symtab_shndx_str_id = Some(self.add_section_name(&b".symtab_shndx"[..])); + self.symtab_shndx_str_id = + Some(self.add_section_name(name.unwrap_or(&b".symtab_shndx"[..]))); self.reserve_section_index() } @@ -991,15 +959,27 @@ 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 + } + + /// Return the size of the dynamic string table. + /// + /// This must be called after [`Self::reserve_dynstr`]. + pub fn dynstr_len(&mut self) -> usize { + if !self.need_dynstr { + return 0; + } + debug_assert_ne!(self.dynstr_offset, 0); + self.dynstr_data.len() } /// Write the dynamic string table. @@ -1016,9 +996,9 @@ impl<'a> Writer<'a> { /// Reserve the section index for the dynamic string table. /// /// This must be called before [`Self::reserve_section_headers`]. - pub fn reserve_dynstr_section_index(&mut self) -> SectionIndex { + pub fn reserve_dynstr_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert_eq!(self.dynstr_index, SectionIndex(0)); - self.dynstr_str_id = Some(self.add_section_name(&b".dynstr"[..])); + self.dynstr_str_id = Some(self.add_section_name(name.unwrap_or(&b".dynstr"[..]))); self.dynstr_index = self.reserve_section_index(); self.dynstr_index } @@ -1100,15 +1080,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.dynsym_num as usize * self.class().sym_size(), self.elf_align, ); + self.dynsym_offset } /// Write the null dynamic symbol. @@ -1175,9 +1156,9 @@ impl<'a> Writer<'a> { /// Reserve the section index for the dynamic symbol table. /// /// This must be called before [`Self::reserve_section_headers`]. - pub fn reserve_dynsym_section_index(&mut self) -> SectionIndex { + pub fn reserve_dynsym_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert_eq!(self.dynsym_index, SectionIndex(0)); - self.dynsym_str_id = Some(self.add_section_name(&b".dynsym"[..])); + self.dynsym_str_id = Some(self.add_section_name(name.unwrap_or(&b".dynsym"[..]))); self.dynsym_index = self.reserve_section_index(); self.dynsym_index } @@ -1200,43 +1181,19 @@ impl<'a> Writer<'a> { sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.dynsym_offset as u64, - sh_size: self.dynsym_num as u64 * self.symbol_size() as u64, + 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.symbol_size() as u64, + sh_entsize: self.class().sym_size() as u64, }); } - fn dyn_size(&self) -> usize { - if self.is_64 { - mem::size_of::>() - } else { - mem::size_of::>() - } - } - - /// Reserve the range for the `.dynamic` section. + /// Reserve a file range for the given number of dynamic entries. /// - /// This function does nothing if `dynamic_num` is zero. - pub fn reserve_dynamic(&mut self, dynamic_num: usize) { - debug_assert_eq!(self.dynamic_offset, 0); - if dynamic_num == 0 { - return; - } - self.dynamic_num = dynamic_num; - self.dynamic_offset = self.reserve(dynamic_num * self.dyn_size(), self.elf_align); - } - - /// Write alignment padding bytes prior to the `.dynamic` section. - /// - /// This function does nothing if the section was not reserved. - pub fn write_align_dynamic(&mut self) { - if self.dynamic_offset == 0 { - return; - } - util::write_align(self.buffer, self.elf_align); - debug_assert_eq!(self.dynamic_offset, self.buffer.len()); + /// Returns the offset of the range. + pub fn reserve_dynamics(&mut self, dynamic_num: usize) -> usize { + self.reserve(dynamic_num * self.class().dyn_size(), self.elf_align) } /// Write a dynamic string entry. @@ -1246,7 +1203,6 @@ impl<'a> Writer<'a> { /// Write a dynamic value entry. pub fn write_dynamic(&mut self, d_tag: u32, d_val: u64) { - debug_assert!(self.dynamic_offset <= self.buffer.len()); let endian = self.endian; if self.is_64 { let d = elf::Dyn64 { @@ -1261,64 +1217,16 @@ impl<'a> Writer<'a> { }; self.buffer.write(&d); } - debug_assert!( - self.dynamic_offset + self.dynamic_num * self.dyn_size() >= self.buffer.len() - ); - } - - /// Reserve the section index for the dynamic table. - pub fn reserve_dynamic_section_index(&mut self) -> SectionIndex { - debug_assert!(self.dynamic_str_id.is_none()); - self.dynamic_str_id = Some(self.add_section_name(&b".dynamic"[..])); - self.reserve_section_index() - } - - /// Write the section header for the dynamic table. - /// - /// This function does nothing if the section index was not reserved. - pub fn write_dynamic_section_header(&mut self, sh_addr: u64) { - if self.dynamic_str_id.is_none() { - return; - } - self.write_section_header(&SectionHeader { - name: self.dynamic_str_id, - sh_type: elf::SHT_DYNAMIC, - sh_flags: (elf::SHF_WRITE | elf::SHF_ALLOC).into(), - sh_addr, - sh_offset: self.dynamic_offset as u64, - sh_size: (self.dynamic_num * self.dyn_size()) as u64, - sh_link: self.dynstr_index.0, - sh_info: 0, - sh_addralign: self.elf_align as u64, - sh_entsize: self.dyn_size() as u64, - }); - } - - 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::>() - } - } } /// Reserve a file range for a SysV hash section. /// /// `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) { - self.hash_size = mem::size_of::>() - + bucket_count as usize * 4 - + chain_count as usize * 4; + pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) -> usize { + self.hash_size = self.class().hash_size(bucket_count, chain_count); self.hash_offset = self.reserve(self.hash_size, self.elf_align); + self.hash_offset } /// Write a SysV hash section. @@ -1350,9 +1258,9 @@ impl<'a> Writer<'a> { } /// Reserve the section index for the SysV hash table. - pub fn reserve_hash_section_index(&mut self) -> SectionIndex { + pub fn reserve_hash_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.hash_str_id.is_none()); - self.hash_str_id = Some(self.add_section_name(&b".hash"[..])); + self.hash_str_id = Some(self.add_section_name(name.unwrap_or(&b".hash"[..]))); self.reserve_section_index() } @@ -1381,12 +1289,17 @@ 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) { - self.gnu_hash_size = mem::size_of::>() - + bloom_count as usize * self.elf_align - + bucket_count as usize * 4 - + symbol_count as usize * 4; + pub fn reserve_gnu_hash( + &mut self, + bloom_count: u32, + bucket_count: u32, + symbol_count: u32, + ) -> usize { + self.gnu_hash_size = self + .class() + .gnu_hash_size(bloom_count, bucket_count, symbol_count); self.gnu_hash_offset = self.reserve(self.gnu_hash_size, self.elf_align); + self.gnu_hash_offset } /// Write a GNU hash section. @@ -1471,9 +1384,9 @@ impl<'a> Writer<'a> { } /// Reserve the section index for the GNU hash table. - pub fn reserve_gnu_hash_section_index(&mut self) -> SectionIndex { + pub fn reserve_gnu_hash_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.gnu_hash_str_id.is_none()); - self.gnu_hash_str_id = Some(self.add_section_name(&b".gnu.hash"[..])); + self.gnu_hash_str_id = Some(self.add_section_name(name.unwrap_or(&b".gnu.hash"[..]))); self.reserve_section_index() } @@ -1501,12 +1414,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. @@ -1528,9 +1442,9 @@ impl<'a> Writer<'a> { } /// Reserve the section index for the `.gnu.version` section. - pub fn reserve_gnu_versym_section_index(&mut self) -> SectionIndex { + pub fn reserve_gnu_versym_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.gnu_versym_str_id.is_none()); - self.gnu_versym_str_id = Some(self.add_section_name(&b".gnu.version"[..])); + self.gnu_versym_str_id = Some(self.add_section_name(name.unwrap_or(&b".gnu.version"[..]))); self.reserve_section_index() } @@ -1547,7 +1461,7 @@ impl<'a> Writer<'a> { sh_flags: elf::SHF_ALLOC.into(), sh_addr, sh_offset: self.gnu_versym_offset as u64, - sh_size: self.dynsym_num as u64 * 2, + 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: 2, @@ -1556,16 +1470,16 @@ 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_size = self.class().gnu_verdef_size(verdef_count, verdaux_count); 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. @@ -1623,9 +1537,10 @@ impl<'a> Writer<'a> { } /// Reserve the section index for the `.gnu.version_d` section. - pub fn reserve_gnu_verdef_section_index(&mut self) -> SectionIndex { + pub fn reserve_gnu_verdef_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.gnu_verdef_str_id.is_none()); - self.gnu_verdef_str_id = Some(self.add_section_name(&b".gnu.version_d"[..])); + self.gnu_verdef_str_id = + Some(self.add_section_name(name.unwrap_or(&b".gnu.version_d"[..]))); self.reserve_section_index() } @@ -1651,16 +1566,16 @@ 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_size = self.class().gnu_verneed_size(verneed_count, vernaux_count); 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. @@ -1718,9 +1633,10 @@ impl<'a> Writer<'a> { } /// Reserve the section index for the `.gnu.version_r` section. - pub fn reserve_gnu_verneed_section_index(&mut self) -> SectionIndex { + pub fn reserve_gnu_verneed_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.gnu_verneed_str_id.is_none()); - self.gnu_verneed_str_id = Some(self.add_section_name(&b".gnu.version_r"[..])); + self.gnu_verneed_str_id = + Some(self.add_section_name(name.unwrap_or(&b".gnu.version_r"[..]))); self.reserve_section_index() } @@ -1746,20 +1662,22 @@ impl<'a> Writer<'a> { } /// Reserve the section index for the `.gnu.attributes` section. - pub fn reserve_gnu_attributes_section_index(&mut self) -> SectionIndex { + pub fn reserve_gnu_attributes_section_index(&mut self, name: Option<&'a [u8]>) -> SectionIndex { debug_assert!(self.gnu_attributes_str_id.is_none()); - self.gnu_attributes_str_id = Some(self.add_section_name(&b".gnu.attributes"[..])); + self.gnu_attributes_str_id = + Some(self.add_section_name(name.unwrap_or(&b".gnu.attributes"[..]))); self.reserve_section_index() } /// Reserve the range for the `.gnu.attributes` section. - pub fn reserve_gnu_attributes(&mut self, gnu_attributes_size: usize) { + pub fn reserve_gnu_attributes(&mut self, gnu_attributes_size: usize) -> usize { debug_assert_eq!(self.gnu_attributes_offset, 0); if gnu_attributes_size == 0 { - return; + return 0; } self.gnu_attributes_size = gnu_attributes_size; self.gnu_attributes_offset = self.reserve(self.gnu_attributes_size, self.elf_align); + self.gnu_attributes_offset } /// Write the section header for the `.gnu.attributes` section. @@ -1797,7 +1715,7 @@ impl<'a> Writer<'a> { /// /// 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(count * self.class().rel_size(is_rela), self.elf_align) } /// Write alignment padding bytes prior to a relocation section. @@ -1865,11 +1783,11 @@ impl<'a> Writer<'a> { sh_flags: elf::SHF_INFO_LINK.into(), sh_addr: 0, sh_offset: offset as u64, - sh_size: (count * self.rel_size(is_rela)) as u64, + sh_size: (count * self.class().rel_size(is_rela)) as u64, sh_link: symtab.0, sh_info: section.0, sh_addralign: self.elf_align as u64, - sh_entsize: self.rel_size(is_rela) as u64, + sh_entsize: self.class().rel_size(is_rela) as u64, }); } @@ -2047,6 +1965,119 @@ impl AttributesWriter { } } +/// 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 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)] diff --git a/src/write/mod.rs b/src/write/mod.rs index 1183cbd9..8d8f3702 100644 --- a/src/write/mod.rs +++ b/src/write/mod.rs @@ -32,7 +32,7 @@ pub mod pe; #[cfg(feature = "xcoff")] mod xcoff; -mod string; +pub(crate) mod string; pub use string::StringId; mod util; @@ -40,7 +40,7 @@ pub use util::*; /// The error type used within the write module. #[derive(Debug, Clone, PartialEq, Eq)] -pub struct Error(String); +pub struct Error(pub(crate) String); impl fmt::Display for Error { #[inline] @@ -66,6 +66,7 @@ pub struct Object<'a> { standard_sections: HashMap, symbols: Vec, symbol_map: HashMap, SymbolId>, + #[cfg(feature = "coff")] stub_symbols: HashMap, comdats: Vec, /// File flags that are specific to each file format. @@ -73,6 +74,7 @@ pub struct Object<'a> { /// The symbol name mangling scheme. pub mangling: Mangling, /// Mach-O "_tlv_bootstrap" symbol. + #[cfg(feature = "macho")] tlv_bootstrap: Option, /// Mach-O CPU subtype. #[cfg(feature = "macho")] @@ -93,10 +95,12 @@ impl<'a> Object<'a> { standard_sections: HashMap::new(), symbols: Vec::new(), symbol_map: HashMap::new(), + #[cfg(feature = "coff")] stub_symbols: HashMap::new(), comdats: Vec::new(), flags: FileFlags::None, mangling: Mangling::default(format, architecture), + #[cfg(feature = "macho")] tlv_bootstrap: None, #[cfg(feature = "macho")] macho_cpu_subtype: None, diff --git a/src/write/string.rs b/src/write/string.rs index 5d4e17b0..c47bf06b 100644 --- a/src/write/string.rs +++ b/src/write/string.rs @@ -78,6 +78,26 @@ impl<'a> StringTable<'a> { } } } + + /// Calculate the size in bytes of the string table. + pub fn size(&self) -> usize { + assert!(self.offsets.is_empty()); + + // TODO: cache this result? + let mut ids: Vec<_> = (0..self.strings.len()).collect(); + sort(&mut ids, 1, &self.strings); + + let mut size = 0; + let mut previous = &[][..]; + for id in ids { + let string = self.strings.get_index(id).unwrap(); + if !previous.ends_with(string) { + size += string.len() + 1; + previous = string; + } + } + size + } } // Multi-key quicksort.