From 6b1edff7d20f11700010ec7838d3eab182c973cf 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 | 5 +- Cargo.toml | 8 +- README.md | 2 +- crates/examples/Cargo.toml | 8 +- crates/examples/src/bin/elfcopy.rs | 1113 ++----------- src/copy/elf.rs | 2399 ++++++++++++++++++++++++++++ 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 | 174 +- src/write/mod.rs | 8 +- src/write/string.rs | 20 + 13 files changed, 2704 insertions(+), 1086 deletions(-) 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 61aaab66..f7a5864e 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 @@ -92,7 +93,7 @@ jobs: with: submodules: true - name: Install rust - run: rustup update 1.65.0 && rustup default 1.65.0 + run: rustup update 1.70.0 && rustup default 1.70.0 - name: Test run: cargo test --verbose --features all 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/README.md b/README.md index 08d9c803..d1a165eb 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ Changes to MSRV are considered breaking changes. We are conservative about chang but sometimes are required to due to dependencies. The MSRV is: * 1.60.0 for the `read` feature and its dependencies. - * 1.65.0 for other features. + * 1.70.0 for other features. ## License diff --git a/crates/examples/Cargo.toml b/crates/examples/Cargo.toml index cca7dc81..b824979f 100644 --- a/crates/examples/Cargo.toml +++ b/crates/examples/Cargo.toml @@ -4,6 +4,9 @@ version = "0.0.0" edition = "2018" [dependencies] +anyhow = "1.0.79" +bumpalo = "3.14.0" +clap = { version = "4.3.24", optional = true } memmap2 = "0.7.1" object = { path = "../..", default-features = false } @@ -13,9 +16,10 @@ glob = "0.3" [features] read = ["object/read"] write = ["object/write"] +copy = ["object/copy", "dep:clap"] wasm = ["object/wasm"] xcoff = ["object/xcoff"] -all = ["read", "write", "wasm", "xcoff"] +all = ["read", "write", "copy", "wasm", "xcoff"] unstable-all = ["all"] default = ["read"] @@ -29,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..170e828f 100644 --- a/crates/examples/src/bin/elfcopy.rs +++ b/crates/examples/src/bin/elfcopy.rs @@ -1,1003 +1,172 @@ -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}; +use std::path::PathBuf; + +use anyhow::{anyhow, Context, Result}; +use bumpalo::Bump; +use clap::parser::ValuesRef; +use clap::{Arg, ArgAction, Command}; + +struct Options<'a> { + in_file: &'a PathBuf, + out_file: &'a PathBuf, + redefine_sym: ValuesRef<'a, String>, + redefine_syms: ValuesRef<'a, PathBuf>, + remove_section: ValuesRef<'a, String>, + rename_section: ValuesRef<'a, String>, +} -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, +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 opts = Options { + in_file: matches.get_one::("in-file").unwrap(), + out_file: matches.get_one::("out-file").unwrap(), + redefine_sym: matches + .get_many::("redefine-sym") + .unwrap_or_default(), + redefine_syms: matches + .get_many::("redefine-syms") + .unwrap_or_default(), + remove_section: matches + .get_many::("remove-section") + .unwrap_or_default(), + rename_section: matches + .get_many::("rename-section") + .unwrap_or_default(), }; - let in_file_path = args.next().unwrap(); - let out_file_path = args.next().unwrap(); + let bump = Bump::new(); - 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); - } - }; + let in_file = fs::File::open(opts.in_file) + .with_context(|| format!("Failed to open input file '{}'", opts.in_file.display()))?; + let in_data = unsafe { memmap2::Mmap::map(&in_file) } + .with_context(|| format!("Failed to map input file '{}'", opts.in_file.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(); + let mut builder = object::copy::elf::Builder::read(in_data) + .with_context(|| format!("Failed to parse input file '{}'", opts.in_file.display()))?; - // 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 }), + let mut redefine_sym = HashMap::<&[u8], &[u8]>::new(); + for arg in opts.redefine_sym { + 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) + ); } + redefine_sym.insert(names[0].as_bytes(), names[1].as_bytes()); } - - 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(); + for filename in opts.redefine_syms { + 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) + ); } + redefine_sym.insert( + bump.alloc_slice_copy(names[0].as_bytes()), + bump.alloc_slice_copy(names[1].as_bytes()), + ); } - - 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); + if !redefine_sym.is_empty() { + for symbol in &mut builder.dynamic_symbols { + if let Some(name) = redefine_sym.get(symbol.name) { + symbol.name = name; } } - 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; + for symbol in &mut builder.symbols { + if let Some(name) = redefine_sym.get(symbol.name) { + symbol.name = name; } } } - // 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]) + for arg in opts.remove_section { + for section in &mut builder.sections { + if section.name == arg.as_bytes() { + section.delete = true; + // TODO: delete associated program header if present } - 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(); + let mut rename_section = HashMap::<&[u8], &[u8]>::new(); + for arg in opts.rename_section { + 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) + ); } - gnu_attributes = writer.data(); - assert_ne!(gnu_attributes.len(), 0); + rename_section.insert(names[0].as_bytes(), names[1].as_bytes()); } - - // 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); + if !rename_section.is_empty() { + for section in &mut builder.sections { + if let Some(name) = rename_section.get(section.name) { + section.name = name; } } } - debug_assert_eq!(writer.reserved_len(), writer.len()); - Ok(out_data) + let out_filename = opts.out_file; + let out_file = fs::File::create(out_filename) + .with_context(|| format!("Failed to create output file '{}'", out_filename.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_filename.display()))?; + Ok(()) } diff --git a/src/copy/elf.rs b/src/copy/elf.rs new file mode 100644 index 00000000..baf897b5 --- /dev/null +++ b/src/copy/elf.rs @@ -0,0 +1,2399 @@ +//! 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 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: usize, + /// 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(), + os_abi: header.e_ident().os_abi, + abi_version: header.e_ident().abi_version, + e_type: header.e_type(endian), + e_machine: header.e_machine(endian), + e_entry: header.e_entry(endian).into(), + e_flags: header.e_flags(endian), + e_phoff: header.e_phoff(endian).into() as usize, + 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 { + 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(), + delete: false, + marker: PhantomData, + }); + } + + 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_NOTE | 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_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)) + }; + builder.sections.0.push(Section { + id, + name: sections.section_name(endian, section)?, + sh_type: section.sh_type(endian), + sh_flags: section.sh_flags(endian).into(), + sh_addr: section.sh_addr(endian).into(), + sh_offset: section.sh_offset(endian).into() as usize, + sh_link_section, + sh_info, + sh_info_section, + sh_addralign: section.sh_addralign(endian).into() as usize, + sh_entsize: section.sh_entsize(endian).into(), + data, + 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 => Dynamic::Auto { tag }, + elf::DT_STRSZ => Dynamic::Auto { tag }, + elf::DT_VERDEFNUM => Dynamic::Auto { tag }, + 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::none(), + 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(); + + 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::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()); + 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)); + } + } + 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 = out_dynsyms + .first() + .map(|sym| out_dynsyms_index[sym.id.index].unwrap().0) + .unwrap_or(0); + let hash_chain_count = writer.dynamic_symbol_count(); + + // GNU hash parameters. + let gnu_hash_index_base = out_dynsyms + .iter() + .position(|sym| sym.gnu_hash.is_some()) + .unwrap_or(0); + let gnu_hash_symbol_base = out_dynsyms + .iter() + .find(|sym| sym.gnu_hash.is_some()) + .map(|sym| out_dynsyms_index[sym.id.index].unwrap().0) + .unwrap_or_else(|| writer.dynamic_symbol_count()); + let gnu_hash_symbol_count = writer.dynamic_symbol_count() - gnu_hash_symbol_base; + + // Assign symbol indices. + let mut out_syms = Vec::with_capacity(self.symbols.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 versym_addr = 0; + let mut verdef_addr = 0; + let mut verneed_addr = 0; + let mut dynsym_addr = 0; + let mut dynstr_addr = 0; + + if !self.segments.0.is_empty() { + // TODO: support program headers in other locations. + if self.e_phoff != writer.reserved_len() { + return Err(Error(format!( + "Unsupported e_phoff value 0x{:x}", + self.e_phoff + ))); + } + let num_segments = self + .segments + .0 + .iter() + .filter(|segment| !segment.delete) + .count(); + writer.reserve_program_headers(num_segments as u32); + } + + let mut alloc_sections = Vec::new(); + if !self.segments.0.is_empty() { + // Reserve alloc sections at original offsets. + alloc_sections = out_sections + .iter() + .enumerate() + .filter_map(|(index, out_section)| { + let section = &self.sections.0[out_section.id.0]; + if section.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + 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() { + return Err(Error(format!( + "Unsupported sh_offset value 0x{:x}, expected at least 0x{:x}", + section.sh_offset, + writer.reserved_len(), + ))); + } + // The input sh_offset needs to be preserved so that offsets in program + // headers are correct. + writer.reserve_until(section.sh_offset); + let offset = match §ion.data { + SectionData::Data(data) => writer.reserve(data.len(), section.sh_addralign), + SectionData::UninitializedData(_) => { + // Note: unaligned input sh_offset was observed in practice. + writer.reserve(0, 1) + } + SectionData::Relocation(relocations) => writer + .reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA), + 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 => { + writer.reserve_hash(self.hash_bucket_count, hash_chain_count) + } + SectionData::GnuHash => writer.reserve_gnu_hash( + self.gnu_hash_bloom_count, + self.gnu_hash_bucket_count, + gnu_hash_symbol_count, + ), + SectionData::GnuVersym => { + versym_addr = section.sh_addr; + writer.reserve_gnu_versym() + } + SectionData::GnuVerdef => { + verdef_addr = section.sh_addr; + writer.reserve_gnu_verdef(verdef_count, verdaux_count) + } + SectionData::GnuVerneed => { + verneed_addr = section.sh_addr; + writer.reserve_gnu_verneed(verneed_count, vernaux_count) + } + _ => { + return Err(Error(format!( + "Unsupported alloc section type {:x}", + section.sh_type + ))); + } + }; + if section.sh_offset != offset { + return Err(Error(format!( + "Unaligned sh_offset value 0x{:x}", + section.sh_offset + ))); + } + } + } + + // 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.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + match §ion.data { + SectionData::Data(data) => { + section.sh_offset = writer.reserve(data.len(), section.sh_addralign); + } + SectionData::UninitializedData(_) => { + section.sh_offset = writer.reserve(0, section.sh_addralign); + } + SectionData::GnuAttributes => { + section.sh_offset = writer.reserve_gnu_attributes(gnu_attributes.len()); + } + // 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.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + let SectionData::Relocation(relocations) = §ion.data else { + continue; + }; + section.sh_offset = + writer.reserve_relocations(relocations.len(), section.sh_type == elf::SHT_RELA); + } + + writer.reserve_shstrtab(); + writer.reserve_section_headers(); + + // Start writing. + writer.write_file_header(&write::elf::FileHeader { + os_abi: self.os_abi, + abi_version: self.abi_version, + e_type: self.e_type, + e_machine: self.e_machine, + e_entry: self.e_entry, + e_flags: self.e_flags, + })?; + + if !self.segments.0.is_empty() { + writer.write_align_program_headers(); + for segment in &self.segments.0 { + if segment.delete { + continue; + } + writer.write_program_header(&write::elf::ProgramHeader { + p_type: segment.p_type, + p_flags: segment.p_flags, + p_offset: segment.p_offset, + p_vaddr: segment.p_vaddr, + p_paddr: segment.p_paddr, + p_filesz: segment.p_filesz, + p_memsz: segment.p_memsz, + p_align: segment.p_align, + }); + } + } + + // Write alloc sections. + if !self.segments.0.is_empty() { + for 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); + 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::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_STRSZ => writer.dynstr_len() as u64, + elf::DT_VERDEFNUM => verdef_count as u64, + 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.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + continue; + } + match §ion.data { + SectionData::Data(data) => { + writer.write_align(section.sh_addralign); + debug_assert_eq!(section.sh_offset, writer.len()); + writer.write(data); + } + SectionData::UninitializedData(_) => { + // Nothing to do. + } + 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.sh_flags & u64::from(elf::SHF_ALLOC) != 0 { + 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::Dynamic(_) => { + let sh_size = match §ion.data { + SectionData::Data(data) => data.len() as u64, + SectionData::UninitializedData(len) => *len, + SectionData::Relocation(relocations) => { + (relocations.len() * writer.rel_size(section.sh_type == elf::SHT_RELA)) + as u64 + } + SectionData::Dynamic(dynamics) => { + (dynamics.len() * writer.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; + } + } + } + + /// 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 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); + } + dynstr.size() + } +} + +/// 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, + // 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 + } +} + +/// 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() + } + + /// 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, + delete: false, + marker: PhantomData, + }); + self.0.last_mut().unwrap() + } +} + +/// 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: usize, + /// 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: usize, + /// 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 + } +} + +/// The data for a [`Section`]. +#[derive(Debug)] +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 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_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() + } +} + +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::none(), + 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)] +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)] +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 none() -> 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 a84064cb..dedf00c9 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 3eec81bd..f41da638 100644 --- a/src/write/elf/object.rs +++ b/src/write/elf/object.rs @@ -586,13 +586,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. @@ -604,7 +604,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..e5308ff7 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, @@ -606,9 +598,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 +673,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 } @@ -858,9 +850,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 } @@ -930,9 +922,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 +984,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 +1021,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 +1105,16 @@ impl<'a> Writer<'a> { /// /// This function does nothing if no dynamic symbols were reserved. /// This must be called after [`Self::reserve_dynamic_symbol_index`]. - pub fn reserve_dynsym(&mut self) { + pub fn reserve_dynsym(&mut self) -> usize { debug_assert_eq!(self.dynsym_offset, 0); if self.dynsym_num == 0 { - return; + return 0; } self.dynsym_offset = self.reserve( self.dynsym_num as usize * self.symbol_size(), self.elf_align, ); + self.dynsym_offset } /// Write the null dynamic symbol. @@ -1175,9 +1181,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 } @@ -1208,7 +1214,8 @@ impl<'a> Writer<'a> { }); } - fn dyn_size(&self) -> usize { + /// Return the size of a dynamic entry. + pub fn dyn_size(&self) -> usize { if self.is_64 { mem::size_of::>() } else { @@ -1216,27 +1223,11 @@ impl<'a> Writer<'a> { } } - /// Reserve the range for the `.dynamic` section. - /// - /// This function does nothing if `dynamic_num` is zero. - pub fn reserve_dynamic(&mut self, dynamic_num: usize) { - 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. + /// Reserve a file range for the given number of dynamic entries. /// - /// 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.dyn_size(), self.elf_align) } /// Write a dynamic string entry. @@ -1246,7 +1237,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,40 +1251,10 @@ 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 { + /// 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::>() @@ -1314,11 +1274,12 @@ impl<'a> Writer<'a> { /// /// `symbol_count` is the number of symbols in the hash, /// not the total number of symbols. - pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) { + pub fn reserve_hash(&mut self, bucket_count: u32, chain_count: u32) -> usize { self.hash_size = mem::size_of::>() + bucket_count as usize * 4 + chain_count as usize * 4; self.hash_offset = self.reserve(self.hash_size, self.elf_align); + self.hash_offset } /// Write a SysV hash section. @@ -1350,9 +1311,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 +1342,18 @@ impl<'a> Writer<'a> { /// /// `symbol_count` is the number of symbols in the hash, /// not the total number of symbols. - pub fn reserve_gnu_hash(&mut self, bloom_count: u32, bucket_count: u32, symbol_count: u32) { + pub fn reserve_gnu_hash( + &mut self, + bloom_count: u32, + bucket_count: u32, + symbol_count: u32, + ) -> usize { self.gnu_hash_size = mem::size_of::>() + bloom_count as usize * self.elf_align + bucket_count as usize * 4 + symbol_count as usize * 4; self.gnu_hash_offset = self.reserve(self.gnu_hash_size, self.elf_align); + self.gnu_hash_offset } /// Write a GNU hash section. @@ -1471,9 +1438,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 +1468,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 +1496,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() } @@ -1556,16 +1524,17 @@ impl<'a> Writer<'a> { } /// Reserve the range for the `.gnu.version_d` section. - pub fn reserve_gnu_verdef(&mut self, verdef_count: usize, verdaux_count: usize) { + pub fn reserve_gnu_verdef(&mut self, verdef_count: usize, verdaux_count: usize) -> usize { debug_assert_eq!(self.gnu_verdef_offset, 0); if verdef_count == 0 { - return; + return 0; } self.gnu_verdef_size = verdef_count * mem::size_of::>() + verdaux_count * mem::size_of::>(); self.gnu_verdef_offset = self.reserve(self.gnu_verdef_size, self.elf_align); self.gnu_verdef_count = verdef_count as u16; self.gnu_verdef_remaining = self.gnu_verdef_count; + self.gnu_verdef_offset } /// Write alignment padding bytes prior to a `.gnu.version_d` section. @@ -1623,9 +1592,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 +1621,17 @@ impl<'a> Writer<'a> { } /// Reserve the range for the `.gnu.version_r` section. - pub fn reserve_gnu_verneed(&mut self, verneed_count: usize, vernaux_count: usize) { + pub fn reserve_gnu_verneed(&mut self, verneed_count: usize, vernaux_count: usize) -> usize { debug_assert_eq!(self.gnu_verneed_offset, 0); if verneed_count == 0 { - return; + return 0; } self.gnu_verneed_size = verneed_count * mem::size_of::>() + vernaux_count * mem::size_of::>(); self.gnu_verneed_offset = self.reserve(self.gnu_verneed_size, self.elf_align); self.gnu_verneed_count = verneed_count as u16; self.gnu_verneed_remaining = self.gnu_verneed_count; + self.gnu_verneed_offset } /// Write alignment padding bytes prior to a `.gnu.version_r` section. @@ -1718,9 +1689,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 +1718,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. diff --git a/src/write/mod.rs b/src/write/mod.rs index 00a9df57..0df3ed3d 100644 --- a/src/write/mod.rs +++ b/src/write/mod.rs @@ -35,7 +35,7 @@ pub mod pe; #[cfg(feature = "xcoff")] mod xcoff; -mod string; +pub(crate) mod string; pub use string::StringId; mod util; @@ -43,7 +43,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] @@ -69,6 +69,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. @@ -76,6 +77,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")] @@ -96,10 +98,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.