From c263f1385d7eecc1a197cc9b5aa8da9e8f47a9a9 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Wed, 3 Jan 2024 17:14:17 +1000 Subject: [PATCH] copy/elf: add Builder The Builder can be created from an existing ELF file, modified, and then written out again. Also change the elfcopy example to use Builder, and add some more options. --- .github/workflows/rust.yml | 3 +- Cargo.toml | 8 +- crates/examples/Cargo.toml | 7 +- crates/examples/src/bin/elfcopy.rs | 1134 +------ crates/examples/src/lib.rs | 3 + crates/examples/src/rewrite.rs | 400 +++ .../examples/testfiles/rewrite/elf/base-noop | 1625 +++++++++ crates/examples/tests/testfiles.rs | 80 +- src/copy/elf.rs | 2909 +++++++++++++++++ src/copy/mod.rs | 40 + src/lib.rs | 3 + src/read/mod.rs | 2 +- src/write/elf/object.rs | 8 +- src/write/elf/writer.rs | 395 +-- src/write/mod.rs | 8 +- src/write/string.rs | 20 + 16 files changed, 5438 insertions(+), 1207 deletions(-) create mode 100644 crates/examples/src/rewrite.rs create mode 100644 crates/examples/testfiles/rewrite/elf/base-noop create mode 100644 src/copy/elf.rs create mode 100644 src/copy/mod.rs 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.