From 9f4cc2f542aad2e2cf82d1e292ecd071ddc80aea Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 24 Dec 2022 03:10:12 -0500 Subject: [PATCH] Add `elf split`; rework asm generation `elf split` allows splitting an ELF directly into relocatable object files --- src/cmd/ar.rs | 6 +- src/cmd/elf.rs | 216 +++++++++++------ src/util/asm.rs | 594 ++++++++++++++++++---------------------------- src/util/elf.rs | 381 ++++++++++++++++------------- src/util/map.rs | 3 +- src/util/mod.rs | 1 + src/util/obj.rs | 100 ++++++-- src/util/split.rs | 233 ++++++++++++++++++ 8 files changed, 909 insertions(+), 625 deletions(-) create mode 100644 src/util/split.rs diff --git a/src/cmd/ar.rs b/src/cmd/ar.rs index f8e4807..b0d4844 100644 --- a/src/cmd/ar.rs +++ b/src/cmd/ar.rs @@ -7,7 +7,7 @@ use std::{ use anyhow::{Context, Error, Result}; use argh::FromArgs; -use object::{Object, ObjectSymbol}; +use object::{Object, ObjectSymbol, SymbolScope}; #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing static libraries. @@ -52,7 +52,7 @@ fn create(args: CreateArgs) -> Result<()> { Some(rsp_file) => { let reader = BufReader::new( File::open(rsp_file) - .with_context(|| format!("Failed to open file '{}'", rsp_file))?, + .with_context(|| format!("Failed to open file '{rsp_file}'"))?, ); for result in reader.lines() { let line = result?; @@ -92,7 +92,7 @@ fn create(args: CreateArgs) -> Result<()> { .with_context(|| format!("Failed to mmap object file: '{}'", path.to_string_lossy()))?; let obj = object::File::parse(map.as_ref())?; for symbol in obj.symbols() { - if symbol.is_global() { + if symbol.scope() == SymbolScope::Dynamic { entries.push(symbol.name_bytes()?.to_vec()); } } diff --git a/src/cmd/elf.rs b/src/cmd/elf.rs index dcaee53..6a7a260 100644 --- a/src/cmd/elf.rs +++ b/src/cmd/elf.rs @@ -1,7 +1,7 @@ use std::{ - collections::{btree_map::Entry, BTreeMap}, + collections::{btree_map, hash_map, BTreeMap, HashMap}, fs, - fs::File, + fs::{DirBuilder, File}, io::{BufWriter, Write}, path::PathBuf, }; @@ -14,7 +14,12 @@ use object::{ SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection, }; -use crate::util::{asm::write_asm, elf::process_elf}; +use crate::util::{ + asm::write_asm, + elf::{process_elf, write_elf}, + obj::ObjKind, + split::split_obj, +}; #[derive(FromArgs, PartialEq, Debug)] /// Commands for processing ELF files. @@ -29,6 +34,7 @@ pub struct Args { enum SubCommand { Disasm(DisasmArgs), Fixup(FixupArgs), + Split(SplitArgs), } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -39,8 +45,8 @@ pub struct DisasmArgs { /// input file elf_file: PathBuf, #[argh(positional)] - /// output directory - out_dir: PathBuf, + /// output file (.o) or directory (.elf) + out: PathBuf, } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -55,55 +61,132 @@ pub struct FixupArgs { out_file: PathBuf, } +#[derive(FromArgs, PartialEq, Eq, Debug)] +/// Splits an executable ELF into relocatable objects. +#[argh(subcommand, name = "split")] +pub struct SplitArgs { + #[argh(positional)] + /// input file + in_file: PathBuf, + #[argh(positional)] + /// output directory + out_dir: PathBuf, +} + pub fn run(args: Args) -> Result<()> { match args.command { SubCommand::Disasm(c_args) => disasm(c_args), SubCommand::Fixup(c_args) => fixup(c_args), + SubCommand::Split(c_args) => split(c_args), } } fn disasm(args: DisasmArgs) -> Result<()> { let obj = process_elf(&args.elf_file)?; - write_asm(&args.out_dir, &obj)?; - for unit in obj.link_order { - let name = format!("$(OBJ_DIR)/asm/{}", file_name_from_unit(&unit)); - println!(" {name: <70}\\"); + match obj.kind { + ObjKind::Executable => { + let split_objs = split_obj(&obj)?; + + let asm_dir = args.out.join("asm"); + let include_dir = args.out.join("include"); + DirBuilder::new().recursive(true).create(&include_dir)?; + fs::write(&include_dir.join("macros.inc"), include_bytes!("../../assets/macros.inc"))?; + + for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { + let out_path = asm_dir.join(file_name_from_unit(unit, ".s")); + if let Some(parent) = out_path.parent() { + DirBuilder::new().recursive(true).create(parent)?; + } + let mut w = BufWriter::new(File::create(out_path)?); + write_asm(&mut w, split_obj)?; + + let name = format!("$(OBJ_DIR)/asm/{}", file_name_from_unit(unit, ".o")); + println!(" {name: <70}\\"); + } + } + ObjKind::Relocatable => { + if let Some(parent) = args.out.parent() { + DirBuilder::new().recursive(true).create(parent)?; + } + let mut w = BufWriter::new(File::create(args.out)?); + write_asm(&mut w, &obj)?; + } } Ok(()) } -fn file_name_from_unit(str: &str) -> String { +fn split(args: SplitArgs) -> Result<()> { + let obj = process_elf(&args.in_file)?; + + let mut file_map = HashMap::::new(); + + let split_objs = split_obj(&obj)?; + for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) { + let out_obj = write_elf(split_obj)?; + match file_map.entry(unit.clone()) { + hash_map::Entry::Occupied(_) => { + return Err(Error::msg(format!("Duplicate file {unit}"))); + } + hash_map::Entry::Vacant(e) => e.insert(out_obj), + }; + } + + let mut rsp_file = BufWriter::new(File::create("rsp")?); + for unit in &obj.link_order { + let object = file_map + .get(unit) + .ok_or_else(|| Error::msg(format!("Failed to find object file for unit '{unit}'")))?; + let out_path = args.out_dir.join(file_name_from_unit(unit, ".o")); + writeln!(rsp_file, "{}", out_path.to_string_lossy())?; + if let Some(parent) = out_path.parent() { + DirBuilder::new().recursive(true).create(parent)?; + } + let mut file = BufWriter::new(File::create(out_path)?); + object.write_stream(&mut file).map_err(|e| Error::msg(format!("{e:?}")))?; + file.flush()?; + } + rsp_file.flush()?; + Ok(()) +} + +fn file_name_from_unit(str: &str, suffix: &str) -> String { + let str = str.strip_suffix(ASM_SUFFIX).unwrap_or(str); let str = str.strip_prefix("C:").unwrap_or(str); let str = str .strip_suffix(".c") .or_else(|| str.strip_suffix(".cp")) .or_else(|| str.strip_suffix(".cpp")) .or_else(|| str.strip_suffix(".s")) + .or_else(|| str.strip_suffix(".o")) .unwrap_or(str); let str = str.replace('\\', "/"); - format!("{}.o", str.strip_prefix('/').unwrap_or(&str)) + let str = str.strip_prefix('/').unwrap_or(&str); + format!("{str}{suffix}") } -const ASM_SUFFIX: &[u8] = " (asm)".as_bytes(); +const ASM_SUFFIX: &str = " (asm)"; fn fixup(args: FixupArgs) -> Result<()> { - let in_buf = fs::read(&args.in_file).context("Failed to open input file")?; + let in_buf = fs::read(&args.in_file).with_context(|| { + format!("Failed to open input file: '{}'", args.in_file.to_string_lossy()) + })?; let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?; let mut out_file = object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness()); - // Write file symbol(s) first + // Write file symbol first let mut file_symbol_found = false; for symbol in in_file.symbols() { if symbol.kind() != SymbolKind::File { continue; } let mut out_symbol = to_write_symbol(&symbol, &[])?; - out_symbol.name.append(&mut ASM_SUFFIX.to_vec()); + out_symbol.name.append(&mut ASM_SUFFIX.as_bytes().to_vec()); out_file.add_symbol(out_symbol); file_symbol_found = true; break; } + // Create a file symbol if not found if !file_symbol_found { let file_name = args.in_file.file_name().ok_or_else(|| { Error::msg(format!("'{}' is not a file path", args.in_file.to_string_lossy())) @@ -112,7 +195,7 @@ fn fixup(args: FixupArgs) -> Result<()> { Error::msg(format!("'{}' is not valid UTF-8", file_name.to_string_lossy())) })?; let mut name_bytes = file_name.as_bytes().to_vec(); - name_bytes.append(&mut ASM_SUFFIX.to_vec()); + name_bytes.append(&mut ASM_SUFFIX.as_bytes().to_vec()); out_file.add_symbol(object::write::Symbol { name: name_bytes, value: 0, @@ -163,11 +246,11 @@ fn fixup(args: FixupArgs) -> Result<()> { symbol_ids.push(Some(symbol_id)); if symbol.size() != 0 { if let Some(section_id) = section_id { - let map = match addr_to_sym.entry(section_id) { - Entry::Vacant(e) => e.insert(BTreeMap::new()), - Entry::Occupied(e) => e.into_mut(), - }; - map.insert(symbol.address() as u32, symbol_id); + match addr_to_sym.entry(section_id) { + btree_map::Entry::Vacant(e) => e.insert(BTreeMap::new()), + btree_map::Entry::Occupied(e) => e.into_mut(), + } + .insert(symbol.address() as u32, symbol_id); } } } @@ -179,53 +262,42 @@ fn fixup(args: FixupArgs) -> Result<()> { None => continue, }; for (addr, reloc) in section.relocations() { - let mut symbol = match reloc.target() { + let mut target_symbol_id = match reloc.target() { RelocationTarget::Symbol(idx) => match symbol_ids[idx.0] { - Some(id) => id, + Some(id) => Ok(id), None => { let in_symbol = in_file.symbol_by_index(idx)?; match in_symbol.kind() { - SymbolKind::Section => { - let section_idx = match in_symbol.section_index() { - Some(id) => id, - None => { - return Err(Error::msg("Missing section for relocation")) - } - }; - let section_id = match section_ids[section_idx.0] { - Some(id) => id, - None => { - return Err(Error::msg("Missing section for relocation")) - } - }; - out_file.section_symbol(section_id) - } - _ => return Err(Error::msg("Missing symbol for relocation")), + SymbolKind::Section => in_symbol + .section_index() + .ok_or_else(|| Error::msg("Section symbol without section")) + .and_then(|section_idx| { + section_ids[section_idx.0].ok_or_else(|| { + Error::msg("Relocation against stripped section") + }) + }) + .map(|section_idx| out_file.section_symbol(section_idx)), + _ => Err(Error::msg("Missing symbol for relocation")), } } }, - RelocationTarget::Section(idx) => { - let section_id = match section_ids[idx.0] { - Some(id) => id, - None => return Err(Error::msg("Missing section for relocation")), - }; - out_file.section_symbol(section_id) - } - RelocationTarget::Absolute => todo!("Absolute relocation target"), - _ => return Err(Error::msg("Invalid relocation target")), - }; - let mut addend = reloc.addend(); + RelocationTarget::Section(section_idx) => section_ids[section_idx.0] + .ok_or_else(|| Error::msg("Relocation against stripped section")) + .map(|section_id| out_file.section_symbol(section_id)), + target => Err(Error::msg(format!("Invalid relocation target '{target:?}'"))), + }?; // Attempt to replace section symbols with direct symbol references - let target_sym = out_file.symbol(symbol); + let mut addend = reloc.addend(); + let target_sym = out_file.symbol(target_symbol_id); if target_sym.kind == SymbolKind::Section { - if let Some(new_symbol_id) = target_sym + if let Some(&new_symbol_id) = target_sym .section .id() .and_then(|id| addr_to_sym.get(&id)) .and_then(|map| map.get(&(addend as u32))) { - symbol = *new_symbol_id; + target_symbol_id = new_symbol_id; addend = 0; } } @@ -242,14 +314,15 @@ fn fixup(args: FixupArgs) -> Result<()> { size: reloc.size(), kind, encoding: reloc.encoding(), - symbol, + symbol: target_symbol_id, addend, })?; } } - let mut out = - BufWriter::new(File::create(&args.out_file).context("Failed to create out file")?); + let mut out = BufWriter::new(File::create(&args.out_file).with_context(|| { + format!("Failed to create output file: '{}'", args.out_file.to_string_lossy()) + })?); out_file.write_stream(&mut out).map_err(|e| Error::msg(format!("{e:?}")))?; out.flush()?; Ok(()) @@ -259,24 +332,25 @@ fn to_write_symbol_section( section: SymbolSection, section_ids: &[Option], ) -> Result { - Ok(match section { - SymbolSection::None => object::write::SymbolSection::None, - SymbolSection::Absolute => object::write::SymbolSection::Absolute, - SymbolSection::Common => object::write::SymbolSection::Common, - SymbolSection::Section(idx) => match section_ids.get(idx.0).and_then(|opt| *opt) { - Some(section_id) => object::write::SymbolSection::Section(section_id), - None => return Err(Error::msg("Missing symbol section")), - }, - _ => object::write::SymbolSection::Undefined, - }) + match section { + SymbolSection::None => Ok(object::write::SymbolSection::None), + SymbolSection::Absolute => Ok(object::write::SymbolSection::Absolute), + SymbolSection::Common => Ok(object::write::SymbolSection::Common), + SymbolSection::Section(idx) => section_ids + .get(idx.0) + .and_then(|&opt| opt) + .map(object::write::SymbolSection::Section) + .ok_or_else(|| Error::msg("Missing symbol section")), + _ => Ok(object::write::SymbolSection::Undefined), + } } fn to_write_symbol_flags(flags: SymbolFlags) -> Result> { - Ok(match flags { - SymbolFlags::Elf { st_info, st_other } => SymbolFlags::Elf { st_info, st_other }, - SymbolFlags::None => SymbolFlags::None, - _ => return Err(Error::msg("Unexpected symbol flags")), - }) + match flags { + SymbolFlags::Elf { st_info, st_other } => Ok(SymbolFlags::Elf { st_info, st_other }), + SymbolFlags::None => Ok(SymbolFlags::None), + _ => Err(Error::msg("Unexpected symbol flags")), + } } fn to_write_symbol( diff --git a/src/util/asm.rs b/src/util/asm.rs index b94d8f6..a080f08 100644 --- a/src/util/asm.rs +++ b/src/util/asm.rs @@ -1,18 +1,15 @@ use std::{ cmp::{min, Ordering}, - collections::{btree_map, hash_map::Entry, BTreeMap, HashMap}, - fs, - fs::{DirBuilder, File}, - io::{BufWriter, Write}, - path::Path, + collections::{btree_map, BTreeMap}, + io::Write, }; use anyhow::{Error, Result}; use ppc750cl::{disasm_iter, Argument, Ins, Opcode}; use crate::util::obj::{ - ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags, - ObjSymbolKind, + nested_push, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, + ObjSymbolFlags, ObjSymbolKind, }; #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -28,106 +25,110 @@ struct SymbolEntry { kind: SymbolEntryKind, } -pub fn write_asm>(path: P, obj: &ObjInfo) -> Result<()> { - let mut file_map = HashMap::>::new(); - - let asm_dir = path.as_ref().join("asm"); - let include_dir = path.as_ref().join("include"); - DirBuilder::new().recursive(true).create(&include_dir)?; - fs::write(&include_dir.join("macros.inc"), include_bytes!("../../assets/macros.inc"))?; - - for unit in &obj.link_order { - let w = match file_map.entry(unit.clone()) { - Entry::Occupied(_) => { - return Err(Error::msg(format!("Duplicate file {unit}"))); - } - Entry::Vacant(e) => { - let file_path = asm_dir.join(file_name_from_unit(unit)); - if let Some(parent) = file_path.parent() { - DirBuilder::new().recursive(true).create(parent)?; - } - e.insert(BufWriter::new(File::create(file_path)?)) - } - }; - writeln!(w, ".include \"macros.inc\"")?; - writeln!(w, ".file \"{}\"", unit.replace('\\', "\\\\"))?; +pub fn write_asm(w: &mut W, obj: &ObjInfo) -> Result<()> { + writeln!(w, ".include \"macros.inc\"")?; + if !obj.name.is_empty() { + writeln!(w, ".file \"{}\"", obj.name.replace('\\', "\\\\"))?; } - let mut symbols = Vec::::new(); - let mut addr_sym = BTreeMap::>::new(); - for section in &obj.sections { - for symbol in §ion.symbols { - let symbol_index = symbols.len(); - symbols.push(symbol.clone()); - let symbol_start = symbol.address as u32; - let symbol_end = (symbol.address + symbol.size) as u32; + // We'll append generated symbols to the end + let mut symbols: Vec = obj.symbols.clone(); + let mut section_entries: Vec>> = vec![]; + let mut section_relocations: Vec> = vec![]; + for (section_idx, section) in obj.sections.iter().enumerate() { + // Build symbol start/end entries + let mut entries = BTreeMap::>::new(); + for (symbol_index, symbol) in obj.symbols_for_section(section_idx) { + if symbol.kind == ObjSymbolKind::Section { + continue; + } + nested_push(&mut entries, symbol.address as u32, SymbolEntry { + index: symbol_index, + kind: SymbolEntryKind::Start, + }); if symbol.size > 0 { - match addr_sym.entry(symbol_start) { - btree_map::Entry::Occupied(mut e) => { - e.get_mut().push(SymbolEntry { - index: symbol_index, - kind: SymbolEntryKind::Start, + nested_push(&mut entries, (symbol.address + symbol.size) as u32, SymbolEntry { + index: symbol_index, + kind: SymbolEntryKind::End, + }); + } + } + + let mut relocations = section.build_relocation_map()?; + + // Generate local jump labels + if section.kind == ObjSectionKind::Code { + for ins in disasm_iter(§ion.data, section.address as u32) { + if let Some(address) = ins.branch_dest() { + if ins.field_AA() + || (address as u64) < section.address + || (address as u64) >= section.address + section.size + { + continue; + } + + // Replace section-relative jump relocations (generated by GCC) + // These aren't possible to express accurately in GNU assembler + if matches!(relocations.get(&ins.addr), Some(reloc) if reloc.addend == 0) { + continue; + } + + let vec = match entries.entry(address) { + btree_map::Entry::Occupied(e) => e.into_mut(), + btree_map::Entry::Vacant(e) => e.insert(vec![]), + }; + let mut target_symbol_idx = vec + .iter() + .find(|e| e.kind == SymbolEntryKind::Label) + .or_else(|| vec.iter().find(|e| e.kind == SymbolEntryKind::Start)) + .map(|e| e.index); + if target_symbol_idx.is_none() { + let display_address = address as u64 + section.original_address; + let symbol_idx = symbols.len(); + symbols.push(ObjSymbol { + name: format!(".L_{display_address:08X}"), + address: display_address, + section: Some(section_idx), + size_known: true, + ..Default::default() }); + vec.push(SymbolEntry { index: symbol_idx, kind: SymbolEntryKind::Label }); + target_symbol_idx = Some(symbol_idx); } - btree_map::Entry::Vacant(e) => { - e.insert(vec![SymbolEntry { - index: symbol_index, - kind: SymbolEntryKind::Start, - }]); - } - } - match addr_sym.entry(symbol_end) { - btree_map::Entry::Occupied(mut e) => { - // Always push first - e.get_mut().insert(0, SymbolEntry { - index: symbol_index, - kind: SymbolEntryKind::End, + if let Some(symbol_idx) = target_symbol_idx { + relocations.insert(ins.addr, ObjReloc { + kind: ObjRelocKind::PpcRel24, + address: ins.addr as u64, + target_symbol: symbol_idx, + addend: 0, }); } - btree_map::Entry::Vacant(e) => { - e.insert(vec![SymbolEntry { - index: symbol_index, - kind: SymbolEntryKind::End, - }]); - } - } - } else { - match addr_sym.entry(symbol_start) { - btree_map::Entry::Occupied(mut e) => { - e.get_mut().push(SymbolEntry { - index: symbol_index, - kind: SymbolEntryKind::Label, - }); - } - btree_map::Entry::Vacant(e) => { - e.insert(vec![SymbolEntry { - index: symbol_index, - kind: SymbolEntryKind::Label, - }]); - } } } } - // Generate labels for .text relocations + section_entries.push(entries); + section_relocations.push(relocations); + } + + // Generate labels for jump tables & relative data relocations + for section in &obj.sections { + if !matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData) { + continue; + } + for reloc in §ion.relocations { - let target_section = match &reloc.target_section { - Some(v) => v, - None => continue, - }; - let section = match obj.sections.iter().find(|s| &s.name == target_section) { - Some(v) => v, - None => continue, - }; - match section.kind { - ObjSectionKind::Code => {} - _ => continue, - } - if reloc.target.addend == 0 { + if reloc.addend == 0 { continue; } - let address = (reloc.target.address as i64 + reloc.target.addend) as u64; - let vec = match addr_sym.entry(address as u32) { + let target = &obj.symbols[reloc.target_symbol]; + let target_section_idx = match target.section { + Some(v) => v, + None => continue, + }; + let target_section = &obj.sections[target_section_idx]; + let address = (target.address as i64 + reloc.addend) as u64; + let vec = match section_entries[target_section_idx].entry(address as u32) { btree_map::Entry::Occupied(e) => e.into_mut(), btree_map::Entry::Vacant(e) => e.insert(vec![]), }; @@ -135,207 +136,100 @@ pub fn write_asm>(path: P, obj: &ObjInfo) -> Result<()> { .iter() .any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start) { - let symbol_index = symbols.len(); + let display_address = address + target_section.original_address; + let symbol_idx = symbols.len(); symbols.push(ObjSymbol { - name: format!(".L_{address:8X}"), - demangled_name: None, - address, - section_address: address - section.address, - size: 0, + name: format!(".L_{display_address:08X}"), + address: display_address, + section: Some(target_section_idx), size_known: true, - flags: Default::default(), - addend: 0, - kind: ObjSymbolKind::Unknown, + ..Default::default() }); - vec.push(SymbolEntry { index: symbol_index, kind: SymbolEntryKind::Label }); - } - } - - // Generate local jump labels - for ins in disasm_iter(§ion.data, section.address as u32) { - if let Some(address) = ins.branch_dest() { - let section = - match obj.sections.iter().find(|s| { - s.address <= address as u64 && (s.address + s.size) > address as u64 - }) { - Some(s) => s, - None => continue, - }; - let vec = match addr_sym.entry(address) { - btree_map::Entry::Occupied(e) => e.into_mut(), - btree_map::Entry::Vacant(e) => e.insert(vec![]), - }; - if !vec - .iter() - .any(|e| e.kind == SymbolEntryKind::Label || e.kind == SymbolEntryKind::Start) - { - let symbol_index = symbols.len(); - symbols.push(ObjSymbol { - name: format!(".L_{address:8X}"), - demangled_name: None, - address: address as u64, - section_address: address as u64 - section.address, - size: 0, - size_known: true, - flags: Default::default(), - addend: 0, - kind: ObjSymbolKind::Unknown, - }); - vec.push(SymbolEntry { index: symbol_index, kind: SymbolEntryKind::Label }); - } + vec.push(SymbolEntry { index: symbol_idx, kind: SymbolEntryKind::Label }); } } } for section in &obj.sections { - log::info!( - "Writing section {} ({:#10X} - {:#10X})", - section.name, - section.address, - section.address + section.size - ); + let entries = §ion_entries[section.index]; + let relocations = §ion_relocations[section.index]; let mut current_address = section.address as u32; let section_end = (section.address + section.size) as u32; - let mut file_iter = obj.splits.range(current_address..).peekable(); + let subsection = + obj.sections.iter().take(section.index).filter(|s| s.name == section.name).count(); - let mut relocations = BTreeMap::::new(); - for reloc in §ion.relocations { - let address = reloc.address as u32; - match relocations.entry(address) { - btree_map::Entry::Vacant(e) => { - e.insert(reloc.clone()); - } - btree_map::Entry::Occupied(_) => { - return Err(Error::msg(format!("Duplicate relocation @ {address:#10X}"))); - } - } - } - - let mut subsection = 0; - let mut current_unit = String::new(); loop { if current_address >= section_end { break; } - let (file_addr, unit) = match file_iter.next() { - Some((addr, unit)) => (*addr, unit), - None => return Err(Error::msg("No file found")), - }; - if file_addr > current_address { - return Err(Error::msg(format!( - "Gap in files: {} @ {:#10X}, {} @ {:#10X}", - section.name, section.address, unit, file_addr - ))); - } - let mut file_end = section_end; - if let Some((next_addr, _)) = file_iter.peek() { - file_end = min(**next_addr, section_end); - } - if unit == ¤t_unit { - subsection += 1; - } else { - current_unit = unit.clone(); - subsection = 0; - } - - let w = write_section_header( - &mut file_map, - unit, - section, - subsection, - current_address, - file_end, - )?; + write_section_header(w, section, subsection, current_address, section_end)?; match section.kind { - ObjSectionKind::Code | ObjSectionKind::Data => { + ObjSectionKind::Code | ObjSectionKind::Data | ObjSectionKind::ReadOnlyData => { write_data( w, &symbols, - &addr_sym, - &relocations, + entries, + relocations, section, current_address, - file_end, + section_end, + §ion_entries, )?; } ObjSectionKind::Bss => { - write_bss(w, &symbols, &addr_sym, current_address, file_end)?; + write_bss(w, &symbols, entries, current_address, section_end)?; } } - current_address = file_end; + + // Write end of symbols + if let Some(entries) = entries.get(§ion_end) { + for entry in entries { + if entry.kind != SymbolEntryKind::End { + continue; + } + write_symbol_entry(w, &symbols, entry)?; + } + } + + current_address = section_end; } } - for (_, mut w) in file_map { - w.flush()?; - } + w.flush()?; Ok(()) } -fn write_code_chunk( - w: &mut BufWriter, +fn write_code_chunk( + w: &mut W, symbols: &[ObjSymbol], - sym_map: &BTreeMap>, + _entries: &BTreeMap>, relocations: &BTreeMap, section: &ObjSection, address: u32, data: &[u8], ) -> Result<()> { for ins in disasm_iter(data, address) { - let mut reloc = relocations.get(&ins.addr); - let mut generated_reloc: Option = None; - - // HACK: GCC-built objects generate section-relative jump relocations, - // which aren't always possible to express in GNU assembler accurately, - // specifically when dealing with multiple sections with the same name. - // Use a (hacky) heuristic to clear them so we generate a local label jump below. - if let Some(rel) = reloc { - if rel.target.addend != 0 - && matches!(rel.kind, ObjRelocKind::PpcRel14 | ObjRelocKind::PpcRel24) - { - reloc = None; - } - } - - // If this is a branch instruction, automatically "relocate" to a label. - // Local branch labels are generated above. - if reloc.is_none() { - if let Some(symbol_entry) = - ins.branch_dest().and_then(|dest| sym_map.get(&dest)).and_then(|entries| { - entries - .iter() - .find(|e| e.kind == SymbolEntryKind::Label) - .or_else(|| entries.iter().find(|e| e.kind == SymbolEntryKind::Start)) - }) - { - let symbol = &symbols[symbol_entry.index]; - generated_reloc = Some(ObjReloc { - kind: ObjRelocKind::Absolute, - address: ins.addr as u64, - target: symbol.clone(), - target_section: None, - }); - } - } - + let reloc = relocations.get(&ins.addr); let file_offset = section.file_offset + (ins.addr as u64 - section.address); - write_ins(w, ins, reloc.or(generated_reloc.as_ref()), file_offset)?; + write_ins(w, symbols, ins, reloc, file_offset, section.original_address)?; } Ok(()) } -fn write_ins( - w: &mut BufWriter, +fn write_ins( + w: &mut W, + symbols: &[ObjSymbol], ins: Ins, reloc: Option<&ObjReloc>, file_offset: u64, + section_address: u64, ) -> Result<()> { write!( w, "/* {:08X} {:08X} {:02X} {:02X} {:02X} {:02X} */\t", - ins.addr, + ins.addr as u64 + section_address, file_offset, (ins.code >> 24) & 0xFF, (ins.code >> 16) & 0xFF, @@ -364,14 +258,14 @@ fn write_ins( match arg { Argument::Uimm(_) | Argument::Simm(_) | Argument::BranchDest(_) => { if let Some(reloc) = reloc { - write_reloc(w, reloc)?; + write_reloc(w, symbols, reloc)?; } else { write!(w, "{arg}")?; } } Argument::Offset(_) => { if let Some(reloc) = reloc { - write_reloc(w, reloc)?; + write_reloc(w, symbols, reloc)?; } else { write!(w, "{arg}")?; } @@ -393,8 +287,8 @@ fn write_ins( Ok(()) } -fn write_reloc(w: &mut BufWriter, reloc: &ObjReloc) -> Result<()> { - write_symbol(w, &reloc.target)?; +fn write_reloc(w: &mut W, symbols: &[ObjSymbol], reloc: &ObjReloc) -> Result<()> { + write_reloc_symbol(w, symbols, reloc)?; match reloc.kind { ObjRelocKind::Absolute | ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14 => { // pass @@ -415,32 +309,27 @@ fn write_reloc(w: &mut BufWriter, reloc: &ObjReloc) -> Result<()> { Ok(()) } -fn write_symbol_entry( - w: &mut BufWriter, +fn write_symbol_entry( + w: &mut W, symbols: &[ObjSymbol], entry: &SymbolEntry, ) -> Result<()> { let symbol = &symbols[entry.index]; - assert_eq!(symbol.addend, 0); // Skip writing certain symbols - if is_skip_symbol(&symbol.name) { + if symbol.kind == ObjSymbolKind::Section { return Ok(()); } - // Comment out linker-generated symbols - let mut start_newline = true; - if entry.kind == SymbolEntryKind::Start && is_linker_symbol(&symbol.name) { - writeln!(w, "\n/* Linker generated")?; - start_newline = false; - } - let symbol_kind = match symbol.kind { ObjSymbolKind::Function => "fn", ObjSymbolKind::Object => "obj", ObjSymbolKind::Unknown => "sym", + ObjSymbolKind::Section => { + return Err(Error::msg(format!("Attempted to write section symbol: {symbol:?}"))) + } }; - let visibility = if symbol.flags.0.contains(ObjSymbolFlags::Weak) { + let scope = if symbol.flags.0.contains(ObjSymbolFlags::Weak) { "weak" } else if symbol.flags.0.contains(ObjSymbolFlags::Global) { "global" @@ -456,11 +345,11 @@ fn write_symbol_entry( } else { write!(w, ".sym ")?; write_symbol_name(w, &symbol.name)?; - writeln!(w, ", {visibility}")?; + writeln!(w, ", {scope}")?; } } SymbolEntryKind::Start => { - if start_newline { + if symbol.kind != ObjSymbolKind::Unknown { writeln!(w)?; } if let Some(name) = &symbol.demangled_name { @@ -468,7 +357,7 @@ fn write_symbol_entry( } write!(w, ".{symbol_kind} ")?; write_symbol_name(w, &symbol.name)?; - writeln!(w, ", {visibility}")?; + writeln!(w, ", {scope}")?; } SymbolEntryKind::End => { write!(w, ".end{symbol_kind} ")?; @@ -477,34 +366,39 @@ fn write_symbol_entry( } } - if entry.kind == SymbolEntryKind::End && is_linker_symbol(&symbol.name) { - writeln!(w, "*/")?; + if matches!(entry.kind, SymbolEntryKind::Start | SymbolEntryKind::Label) + && symbol.flags.0.contains(ObjSymbolFlags::Hidden) + { + write!(w, ".hidden ")?; + write_symbol_name(w, &symbol.name)?; + writeln!(w)?; } Ok(()) } -fn write_data( - w: &mut BufWriter, +fn write_data( + w: &mut W, symbols: &[ObjSymbol], - sym_map: &BTreeMap>, + entries: &BTreeMap>, relocations: &BTreeMap, section: &ObjSection, start: u32, end: u32, + section_entries: &[BTreeMap>], ) -> Result<()> { - let mut sym_iter = sym_map.range(start..end); + let mut entry_iter = entries.range(start..end); let mut reloc_iter = relocations.range(start..end); let mut current_address = start; let mut current_symbol_kind = ObjSymbolKind::Unknown; - let mut sym = sym_iter.next(); + let mut entry = entry_iter.next(); let mut reloc = reloc_iter.next(); let mut begin = true; loop { if current_address == end { break; } - if let Some((sym_addr, vec)) = sym { + if let Some((sym_addr, vec)) = entry { if current_address == *sym_addr { for entry in vec { if entry.kind == SymbolEntryKind::End && begin { @@ -513,7 +407,7 @@ fn write_data( write_symbol_entry(w, symbols, entry)?; } current_symbol_kind = find_symbol_kind(current_symbol_kind, symbols, vec)?; - sym = sym_iter.next(); + entry = entry_iter.next(); } } begin = false; @@ -521,7 +415,9 @@ fn write_data( let symbol_kind = if current_symbol_kind == ObjSymbolKind::Unknown { match section.kind { ObjSectionKind::Code => ObjSymbolKind::Function, - ObjSectionKind::Data | ObjSectionKind::Bss => ObjSymbolKind::Object, + ObjSectionKind::Data | ObjSectionKind::ReadOnlyData | ObjSectionKind::Bss => { + ObjSymbolKind::Object + } } } else { current_symbol_kind @@ -531,18 +427,19 @@ fn write_data( reloc = reloc_iter.next(); match symbol_kind { ObjSymbolKind::Object => { - current_address = write_data_reloc(w, symbols, sym_map, r)?; + current_address = + write_data_reloc(w, symbols, entries, r, section_entries)?; continue; } ObjSymbolKind::Function => { // handled in write_code_chunk } - ObjSymbolKind::Unknown => unreachable!(), + ObjSymbolKind::Unknown | ObjSymbolKind::Section => unreachable!(), } } } - let until = match (sym, reloc) { + let until = match (entry, reloc) { (Some((sym_addr, _)), Some((reloc_addr, _))) => min(*reloc_addr, *sym_addr), (Some((addr, _)), None) | (None, Some((addr, _))) => *addr, (None, None) => end, @@ -552,27 +449,18 @@ fn write_data( if symbol_kind == ObjSymbolKind::Function { if current_address & 3 != 0 || data.len() & 3 != 0 { return Err(Error::msg(format!( - "Unaligned code write @ {:#010X} size {:#X}", + "Unaligned code write @ {} {:#010X} size {:#X}", + section.name, current_address, data.len() ))); } - write_code_chunk(w, symbols, sym_map, relocations, section, current_address, data)?; + write_code_chunk(w, symbols, entries, relocations, section, current_address, data)?; } else { write_data_chunk(w, data)?; } current_address = until; } - - // Write end of symbols - if let Some(entries) = sym_map.get(&end) { - for entry in entries { - if entry.kind != SymbolEntryKind::End { - continue; - } - write_symbol_entry(w, symbols, entry)?; - } - } Ok(()) } @@ -587,7 +475,7 @@ fn find_symbol_kind( match entry.kind { SymbolEntryKind::Start => { let new_kind = symbols[entry.index].kind; - if new_kind != ObjSymbolKind::Unknown { + if !matches!(new_kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section) { if found && new_kind != kind { return Err(Error::msg(format!( "Conflicting symbol kinds found: {kind:?} and {new_kind:?}" @@ -603,7 +491,7 @@ fn find_symbol_kind( Ok(kind) } -fn write_data_chunk(w: &mut BufWriter, data: &[u8]) -> Result<()> { +fn write_data_chunk(w: &mut W, data: &[u8]) -> Result<()> { let remain = data; for chunk in remain.chunks(4) { match chunk.len() { @@ -626,24 +514,27 @@ fn write_data_chunk(w: &mut BufWriter, data: &[u8]) -> Result<()> { Ok(()) } -fn write_data_reloc( - w: &mut BufWriter, +fn write_data_reloc( + w: &mut W, symbols: &[ObjSymbol], - sym_map: &BTreeMap>, + _entries: &BTreeMap>, reloc: &ObjReloc, + section_entries: &[BTreeMap>], ) -> Result { - Ok(match reloc.kind { + match reloc.kind { ObjRelocKind::Absolute => { // Attempt to use .rel macro for relative relocations - if reloc.target.addend != 0 { - let target_addr = (reloc.target.address as i64 + reloc.target.addend) as u32; - if let Some(entry) = sym_map - .get(&target_addr) + if reloc.addend != 0 { + let target = &symbols[reloc.target_symbol]; + let target_addr = (target.address as i64 + reloc.addend) as u32; + if let Some(entry) = target + .section + .and_then(|section_idx| section_entries[section_idx].get(&target_addr)) .and_then(|entries| entries.iter().find(|e| e.kind == SymbolEntryKind::Label)) { let symbol = &symbols[entry.index]; write!(w, "\t.rel ")?; - write_symbol_name(w, &reloc.target.name)?; + write_symbol_name(w, &target.name)?; write!(w, ", ")?; write_symbol_name(w, &symbol.name)?; writeln!(w)?; @@ -651,31 +542,31 @@ fn write_data_reloc( } } write!(w, "\t.4byte ")?; - write_symbol(w, &reloc.target)?; + write_reloc_symbol(w, symbols, reloc)?; writeln!(w)?; - (reloc.address + 4) as u32 + Ok((reloc.address + 4) as u32) } - _ => todo!(), - }) + _ => Err(Error::msg(format!("Unsupported data relocation type {:?}", reloc.kind))), + } } -fn write_bss( - w: &mut BufWriter, +fn write_bss( + w: &mut W, symbols: &[ObjSymbol], - sym_map: &BTreeMap>, + entries: &BTreeMap>, start: u32, end: u32, ) -> Result<()> { - let mut sym_iter = sym_map.range(start..end); + let mut entry_iter = entries.range(start..end); let mut current_address = start; - let mut sym = sym_iter.next(); + let mut entry = entry_iter.next(); let mut begin = true; loop { if current_address == end { break; } - if let Some((sym_addr, vec)) = sym { + if let Some((sym_addr, vec)) = entry { if current_address == *sym_addr { for entry in vec { if entry.kind == SymbolEntryKind::End && begin { @@ -683,116 +574,92 @@ fn write_bss( } write_symbol_entry(w, symbols, entry)?; } - sym = sym_iter.next(); + entry = entry_iter.next(); } } begin = false; - let until = sym.map(|(addr, _)| *addr).unwrap_or(end); + let until = entry.map(|(addr, _)| *addr).unwrap_or(end); let size = until - current_address; if size > 0 { writeln!(w, "\t.skip {size:#X}")?; } current_address = until; } - - // Write end of symbols - if let Some(entries) = sym_map.get(&end) { - for entry in entries { - if entry.kind != SymbolEntryKind::End { - continue; - } - write_symbol_entry(w, symbols, entry)?; - } - } Ok(()) } -fn file_name_from_unit(str: &str) -> String { - let str = str.strip_prefix("C:").unwrap_or(str); - let str = str - .strip_suffix(".c") - .or_else(|| str.strip_suffix(".cp")) - .or_else(|| str.strip_suffix(".cpp")) - .or_else(|| str.strip_suffix(".s")) - .unwrap_or(str); - let str = str.replace('\\', "/"); - format!("{}.s", str.strip_prefix('/').unwrap_or(&str)) -} - -fn write_section_header<'a>( - file_map: &'a mut HashMap>, - unit: &String, +fn write_section_header( + w: &mut W, section: &ObjSection, subsection: usize, start: u32, end: u32, -) -> Result<&'a mut BufWriter> { - let w = file_map - .get_mut(unit) - .ok_or_else(|| Error::msg(format!("Failed to locate file for {unit}")))?; - writeln!(w, "\n# {start:#10X} - {end:#10X}")?; - let alignment = match section.name.as_str() { +) -> Result<()> { + writeln!( + w, + "\n# {:#010X} - {:#010X}", + start as u64 + section.original_address, + end as u64 + section.original_address + )?; + match section.name.as_str() { ".text" if subsection == 0 => { write!(w, "{}", section.name)?; - 4 } - ".data" | ".bss" | ".rodata" if subsection == 0 => { + // .bss excluded to support < r40 devkitPro + ".data" | ".rodata" if subsection == 0 => { write!(w, "{}", section.name)?; - 8 } ".text" | ".init" => { write!(w, ".section {}", section.name)?; write!(w, ", \"ax\"")?; - 4 } ".data" | ".sdata" => { write!(w, ".section {}", section.name)?; write!(w, ", \"wa\"")?; - 8 } ".rodata" | ".sdata2" => { write!(w, ".section {}", section.name)?; write!(w, ", \"a\"")?; - 8 } ".bss" | ".sbss" => { write!(w, ".section {}", section.name)?; write!(w, ", \"wa\", @nobits")?; - 8 } ".sbss2" => { write!(w, ".section {}", section.name)?; write!(w, ", \"a\", @nobits")?; - 8 } ".ctors" | ".dtors" | "extab" | "extabindex" => { write!(w, ".section {}", section.name)?; write!(w, ", \"a\"")?; - 4 } - name => todo!("Section {}", name), + name => return Err(Error::msg(format!("Unknown section {name}"))), }; if subsection != 0 { write!(w, ", unique, {subsection}")?; } writeln!(w)?; - if alignment != 0 { - writeln!(w, ".balign {alignment}")?; + if section.align != 0 { + writeln!(w, ".balign {}", section.align)?; } - Ok(w) + Ok(()) } -fn write_symbol(w: &mut BufWriter, sym: &ObjSymbol) -> std::io::Result<()> { - write_symbol_name(w, &sym.name)?; - match sym.addend.cmp(&0i64) { - Ordering::Greater => write!(w, "+{:#X}", sym.addend), - Ordering::Less => write!(w, "-{:#X}", -sym.addend), +fn write_reloc_symbol( + w: &mut W, + symbols: &[ObjSymbol], + reloc: &ObjReloc, +) -> std::io::Result<()> { + write_symbol_name(w, &symbols[reloc.target_symbol].name)?; + match reloc.addend.cmp(&0i64) { + Ordering::Greater => write!(w, "+{:#X}", reloc.addend), + Ordering::Less => write!(w, "-{:#X}", -reloc.addend), Ordering::Equal => Ok(()), } } -fn write_symbol_name(w: &mut BufWriter, name: &str) -> std::io::Result<()> { +fn write_symbol_name(w: &mut W, name: &str) -> std::io::Result<()> { // TODO more? if name.contains('@') || name.contains('<') { write!(w, "\"{name}\"")?; @@ -802,17 +669,6 @@ fn write_symbol_name(w: &mut BufWriter, name: &str) -> std::io::Result<()> Ok(()) } -#[inline] -fn is_skip_symbol(name: &str) -> bool { - // Avoid generating these, they span across files - matches!(name, "_ctors" | "_dtors") -} - -#[inline] -fn is_linker_symbol(name: &str) -> bool { - matches!(name, "_eti_init_info" | "_rom_copy_info" | "_bss_init_info") -} - #[inline] fn is_illegal_instruction(code: u32) -> bool { matches!(code, 0x43000000 /* bc 24, lt, 0x0 */ | 0xB8030000 /* lmw r0, 0(r3) */) diff --git a/src/util/elf.rs b/src/util/elf.rs index 6dc5902..7b935ce 100644 --- a/src/util/elf.rs +++ b/src/util/elf.rs @@ -7,16 +7,18 @@ use indexmap::IndexMap; use memmap2::MmapOptions; use object::{ elf::{ - R_PPC_ADDR16_HA, R_PPC_ADDR16_HI, R_PPC_ADDR16_LO, R_PPC_EMB_SDA21, R_PPC_REL14, - R_PPC_REL24, + R_PPC_ADDR16_HA, R_PPC_ADDR16_HI, R_PPC_ADDR16_LO, R_PPC_ADDR32, R_PPC_EMB_SDA21, + R_PPC_REL14, R_PPC_REL24, }, - Architecture, Object, ObjectKind, ObjectSection, ObjectSymbol, Relocation, RelocationKind, - RelocationTarget, Section, SectionKind, Symbol, SymbolKind, SymbolSection, + write::{Mangling, SectionId, SymbolId}, + Architecture, BinaryFormat, Endianness, Object, ObjectKind, ObjectSection, ObjectSymbol, + Relocation, RelocationEncoding, RelocationKind, RelocationTarget, SectionKind, Symbol, + SymbolFlags, SymbolKind, SymbolScope, SymbolSection, }; use crate::util::obj::{ - ObjArchitecture, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, - ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, + ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, + ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, }; pub fn process_elf>(path: P) -> Result { @@ -31,13 +33,15 @@ pub fn process_elf>(path: P) -> Result { Architecture::PowerPc => ObjArchitecture::PowerPc, arch => return Err(Error::msg(format!("Unexpected architecture: {arch:?}"))), }; - if obj_file.is_little_endian() { + if obj_file.endianness() != Endianness::Big { return Err(Error::msg("Expected big endian")); } - match obj_file.kind() { - ObjectKind::Executable => {} + let kind = match obj_file.kind() { + ObjectKind::Executable => ObjKind::Executable, + ObjectKind::Relocatable => ObjKind::Relocatable, kind => return Err(Error::msg(format!("Unexpected ELF type: {kind:?}"))), - } + }; + let mut obj_name = String::new(); let mut stack_address: Option = None; let mut stack_end: Option = None; @@ -45,7 +49,36 @@ pub fn process_elf>(path: P) -> Result { let mut arena_lo: Option = None; let mut arena_hi: Option = None; - let mut common: Vec = vec![]; + let mut sections: Vec = vec![]; + let mut section_indexes: Vec> = vec![]; + for section in obj_file.sections() { + let section_kind = match section.kind() { + SectionKind::Text => ObjSectionKind::Code, + SectionKind::Data => ObjSectionKind::Data, + SectionKind::ReadOnlyData => ObjSectionKind::ReadOnlyData, + SectionKind::UninitializedData => ObjSectionKind::Bss, + _ => { + section_indexes.push(None); + continue; + } + }; + section_indexes.push(Some(sections.len())); + sections.push(ObjSection { + name: section.name()?.to_string(), + kind: section_kind, + address: section.address(), + size: section.size(), + data: section.uncompressed_data()?.to_vec(), + align: section.align(), + index: sections.len(), + relocations: vec![], + original_address: 0, // TODO load from abs symbol + file_offset: section.file_range().map(|(v, _)| v).unwrap_or_default(), + }); + } + + let mut symbols: Vec = vec![]; + let mut symbol_indexes: Vec> = vec![]; let mut current_file: Option = None; let mut section_starts = IndexMap::>::new(); for symbol in obj_file.symbols() { @@ -54,23 +87,18 @@ pub fn process_elf>(path: P) -> Result { match symbol_name { "_stack_addr" => { stack_address = Some(symbol.address() as u32); - continue; } "_stack_end" => { stack_end = Some(symbol.address() as u32); - continue; } "_db_stack_addr" => { db_stack_addr = Some(symbol.address() as u32); - continue; } "__ArenaLo" => { arena_lo = Some(symbol.address() as u32); - continue; } "__ArenaHi" => { arena_hi = Some(symbol.address() as u32); - continue; } _ => {} } @@ -79,6 +107,9 @@ pub fn process_elf>(path: P) -> Result { // Detect file boundaries SymbolKind::File => { let file_name = symbol_name.to_string(); + if kind == ObjKind::Relocatable { + obj_name = file_name.clone(); + } match section_starts.entry(file_name.clone()) { indexmap::map::Entry::Occupied(_) => { return Err(Error::msg(format!("Duplicate file name: {file_name}"))); @@ -86,15 +117,13 @@ pub fn process_elf>(path: P) -> Result { indexmap::map::Entry::Vacant(e) => e.insert(Default::default()), }; current_file = Some(file_name); - continue; } // Detect sections within a file SymbolKind::Section => { if let Some(file_name) = ¤t_file { - let sections = match section_starts.get_mut(file_name) { - Some(entries) => entries, - None => return Err(Error::msg("Failed to create entry")), - }; + let sections = section_starts + .get_mut(file_name) + .ok_or_else(|| Error::msg("Failed to create entry"))?; let section_index = symbol .section_index() .ok_or_else(|| Error::msg("Section symbol without section"))?; @@ -102,30 +131,27 @@ pub fn process_elf>(path: P) -> Result { let section_name = section.name()?.to_string(); sections.push((symbol.address(), section_name)); }; - continue; } // Sometimes, the section symbol address is 0, // so attempt to detect it from first symbol within section SymbolKind::Data | SymbolKind::Text => { if let Some(file_name) = ¤t_file { - let section_map = match section_starts.get_mut(file_name) { - Some(entries) => entries, - None => return Err(Error::msg("Failed to create entry")), - }; - let section_index = symbol - .section_index() - .ok_or_else(|| Error::msg("Section symbol without section"))?; + let sections = section_starts + .get_mut(file_name) + .ok_or_else(|| Error::msg("Failed to create entry"))?; + let section_index = symbol.section_index().ok_or_else(|| { + Error::msg(format!("Section symbol without section: {symbol:?}")) + })?; let section = obj_file.section_by_index(section_index)?; let section_name = section.name()?; if let Some((addr, _)) = - section_map.iter_mut().find(|(_, name)| name == section_name) + sections.iter_mut().find(|(_, name)| name == section_name) { if *addr == 0 { *addr = symbol.address(); } }; }; - continue; } _ => match symbol.section() { // Linker generated symbols indicate the end @@ -133,82 +159,60 @@ pub fn process_elf>(path: P) -> Result { current_file = None; } SymbolSection::Section(_) | SymbolSection::Undefined => {} - _ => todo!("Symbol section type {:?}", symbol), + _ => return Err(Error::msg(format!("Unsupported symbol section type {symbol:?}"))), }, } - // Keep track of common symbols - if !symbol.is_common() { + // Generate symbols + if matches!(symbol.kind(), SymbolKind::Null | SymbolKind::File) { + symbol_indexes.push(None); continue; } - common.push(to_obj_symbol(&obj_file, &symbol, 0)?); + symbol_indexes.push(Some(symbols.len())); + symbols.push(to_obj_symbol(&obj_file, &symbol, §ion_indexes)?); } - // Link order is trivially deduced let mut link_order = Vec::::new(); - for file_name in section_starts.keys() { - link_order.push(file_name.clone()); - } - - // Create a map of address -> file splits let mut splits = BTreeMap::::new(); - for (file_name, sections) in section_starts { - for (address, _) in sections { - splits.insert(address as u32, file_name.clone()); + if kind == ObjKind::Executable { + // Link order is trivially deduced + for file_name in section_starts.keys() { + link_order.push(file_name.clone()); } + + // Create a map of address -> file splits + for (file_name, sections) in section_starts { + for (address, _) in sections { + splits.insert(address as u32, file_name.clone()); + } + } + + // TODO rebuild common symbols } - let mut sections: Vec = vec![]; - for section in obj_file.sections() { - let section_index = section.index(); - let section_kind = match section.kind() { - SectionKind::Text => ObjSectionKind::Code, - SectionKind::Data => ObjSectionKind::Data, - SectionKind::ReadOnlyData => ObjSectionKind::Data, - SectionKind::UninitializedData => ObjSectionKind::Bss, - _ => continue, + for (section_idx, section) in obj_file.sections().enumerate() { + let out_section = match section_indexes[section_idx].and_then(|idx| sections.get_mut(idx)) { + Some(s) => s, + None => continue, }; - let name = section.name()?; - log::info!("Processing section {}", name); - let data = section.uncompressed_data()?.to_vec(); - - // Generate symbols - let mut symbols: Vec = vec![]; - for symbol in obj_file.symbols() { - if !matches!(symbol.section_index(), Some(idx) if idx == section_index) { - continue; - } - if symbol.address() == 0 || symbol.name()?.is_empty() { - continue; - } - symbols.push(to_obj_symbol(&obj_file, &symbol, 0)?); - } - // Generate relocations - let mut relocations: Vec = vec![]; for (address, reloc) in section.relocations() { - relocations.push(to_obj_reloc(&obj_file, §ion, &data, address, reloc)?); + out_section.relocations.push(to_obj_reloc( + &obj_file, + &symbol_indexes, + &out_section.data, + address, + reloc, + )?); } - - let file_offset = section.file_range().map(|(v, _)| v).unwrap_or_default(); - sections.push(ObjSection { - name: name.to_string(), - kind: section_kind, - address: section.address(), - size: section.size(), - data, - index: sections.len(), - symbols, - relocations, - file_offset, - }); } Ok(ObjInfo { + kind, architecture, - path: path.as_ref().to_path_buf(), + name: obj_name, + symbols, sections, - common, entry: obj_file.entry() as u32, stack_address, stack_end, @@ -220,10 +224,107 @@ pub fn process_elf>(path: P) -> Result { }) } +pub fn write_elf(obj: &ObjInfo) -> Result { + let mut out_obj = + object::write::Object::new(BinaryFormat::Elf, Architecture::PowerPc, Endianness::Big); + out_obj.set_mangling(Mangling::None); + if !obj.name.is_empty() { + out_obj.add_file_symbol(obj.name.as_bytes().to_vec()); + } + + let mut section_idxs: Vec = Vec::with_capacity(obj.sections.len()); + for section in &obj.sections { + let section_id = + out_obj.add_section(vec![], section.name.as_bytes().to_vec(), match section.kind { + ObjSectionKind::Code => SectionKind::Text, + ObjSectionKind::Data => SectionKind::Data, + ObjSectionKind::ReadOnlyData => SectionKind::ReadOnlyData, + ObjSectionKind::Bss => SectionKind::UninitializedData, + }); + section_idxs.push(section_id); + let out_section = out_obj.section_mut(section_id); + match section.kind { + ObjSectionKind::Bss => { + out_section.append_bss(section.size, section.align); + } + _ => { + out_section.set_data(section.data.clone(), section.align); + } + } + + // Generate section symbol + out_obj.section_symbol(section_id); + + // Add original addresses + if section.original_address != 0 { + // TODO write to metadata? + } + if section.file_offset != 0 { + // TODO write to metadata? + } + } + + // Add symbols + let mut symbol_idxs: Vec = Vec::with_capacity(obj.symbols.len()); + for symbol in &obj.symbols { + let symbol_id = out_obj.add_symbol(object::write::Symbol { + name: symbol.name.as_bytes().to_vec(), + value: symbol.address, + size: symbol.size, + kind: match symbol.kind { + ObjSymbolKind::Unknown => SymbolKind::Null, + ObjSymbolKind::Function => SymbolKind::Text, + ObjSymbolKind::Object => SymbolKind::Data, + ObjSymbolKind::Section => SymbolKind::Section, + }, + scope: if symbol.flags.0.contains(ObjSymbolFlags::Hidden) { + SymbolScope::Linkage + } else if symbol.flags.0.contains(ObjSymbolFlags::Local) { + SymbolScope::Compilation + } else { + SymbolScope::Dynamic + }, + weak: symbol.flags.0.contains(ObjSymbolFlags::Weak), + section: match symbol.section { + None => object::write::SymbolSection::Undefined, + Some(idx) => object::write::SymbolSection::Section(section_idxs[idx]), + }, + flags: SymbolFlags::None, + }); + symbol_idxs.push(symbol_id); + } + + // Add relocations + for section in &obj.sections { + let section_id = section_idxs[section.index]; + for reloc in §ion.relocations { + let symbol_id = symbol_idxs[reloc.target_symbol]; + out_obj.add_relocation(section_id, object::write::Relocation { + offset: reloc.address, + size: 0, + kind: RelocationKind::Elf(match reloc.kind { + ObjRelocKind::Absolute => R_PPC_ADDR32, + ObjRelocKind::PpcAddr16Hi => R_PPC_ADDR16_HI, + ObjRelocKind::PpcAddr16Ha => R_PPC_ADDR16_HA, + ObjRelocKind::PpcAddr16Lo => R_PPC_ADDR16_LO, + ObjRelocKind::PpcRel24 => R_PPC_REL24, + ObjRelocKind::PpcRel14 => R_PPC_REL14, + ObjRelocKind::PpcEmbSda21 => R_PPC_EMB_SDA21, + }), + encoding: RelocationEncoding::Generic, + symbol: symbol_id, + addend: reloc.addend, + })?; + } + } + + Ok(out_obj) +} + fn to_obj_symbol( obj_file: &object::File<'_>, symbol: &Symbol<'_, '_>, - addend: i64, + section_indexes: &[Option], ) -> Result { let section = match symbol.section_index() { Some(idx) => Some(obj_file.section_by_index(idx)?), @@ -255,31 +356,31 @@ fn to_obj_symbol( if symbol.is_weak() { flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Weak); } - let section_address = if let Some(section) = §ion { - symbol.address() - section.address() - } else { - symbol.address() - }; + if symbol.scope() == SymbolScope::Linkage { + flags = ObjSymbolFlagSet(flags.0 | ObjSymbolFlags::Hidden); + } + let section_idx = section.as_ref().and_then(|section| section_indexes[section.index().0]); Ok(ObjSymbol { name: name.to_string(), demangled_name: demangle(name, &Default::default()), address: symbol.address(), - section_address, + section: section_idx, size: symbol.size(), - size_known: symbol.size() != 0, + size_known: true, flags, - addend, kind: match symbol.kind() { SymbolKind::Text => ObjSymbolKind::Function, SymbolKind::Data => ObjSymbolKind::Object, - _ => ObjSymbolKind::Unknown, + SymbolKind::Unknown => ObjSymbolKind::Unknown, + SymbolKind::Section => ObjSymbolKind::Section, + _ => return Err(Error::msg(format!("Unsupported symbol kind: {:?}", symbol.kind()))), }, }) } fn to_obj_reloc( obj_file: &object::File<'_>, - _section: &Section<'_, '_>, + symbol_indexes: &[Option], section_data: &[u8], address: u64, reloc: Relocation, @@ -305,17 +406,10 @@ fn to_obj_reloc( return Err(Error::msg(format!("Unhandled relocation target: {:?}", reloc.target()))); } }; - let target_section = match symbol.section() { - SymbolSection::Common => Some(".comm".to_string()), - SymbolSection::Section(idx) => { - obj_file.section_by_index(idx).and_then(|s| s.name().map(|s| s.to_string())).ok() - } - _ => None, - }; - let target = match symbol.kind() { - SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => { - to_obj_symbol(obj_file, &symbol, reloc.addend()) - } + let target_symbol = symbol_indexes[symbol.index().0] + .ok_or_else(|| Error::msg(format!("Relocation against stripped symbol: {symbol:?}")))?; + let addend = match symbol.kind() { + SymbolKind::Text | SymbolKind::Data | SymbolKind::Unknown => Ok(reloc.addend()), SymbolKind::Section => { let addend = if reloc.has_implicit_addend() { let addend = u32::from_be_bytes( @@ -323,70 +417,23 @@ fn to_obj_reloc( ) as i64; match reloc_kind { ObjRelocKind::Absolute => addend, - _ => todo!(), + _ => { + return Err(Error::msg(format!( + "Unsupported implicit relocation type {reloc_kind:?}" + ))) + } } } else { - let addend = reloc.addend(); - if addend < 0 { - return Err(Error::msg(format!("Negative addend in section reloc: {addend}"))); - } - addend + reloc.addend() }; - // find_section_symbol(&obj_file, &symbol, addend as u64) - to_obj_symbol(obj_file, &symbol, addend) + if addend < 0 { + return Err(Error::msg(format!("Negative addend in section reloc: {addend}"))); + } + Ok(addend) } _ => Err(Error::msg(format!("Unhandled relocation symbol type {:?}", symbol.kind()))), }?; - let address = address & !3; // FIXME round down for instruction - let reloc_data = ObjReloc { kind: reloc_kind, address, target, target_section }; + let address = address & !3; // TODO hack: round down for instruction + let reloc_data = ObjReloc { kind: reloc_kind, address, target_symbol, addend }; Ok(reloc_data) } - -// TODO needed? -#[allow(dead_code)] -fn find_section_symbol( - obj_file: &object::File<'_>, - target: &Symbol<'_, '_>, - addend: u64, -) -> Result { - let section_index = - target.section_index().ok_or_else(|| Error::msg("Unknown section index"))?; - let section = obj_file.section_by_index(section_index)?; - let target_address = target.address() + addend; - - let mut closest_symbol: Option> = None; - for symbol in obj_file.symbols() { - if !matches!(symbol.section_index(), Some(idx) if idx == section_index) { - continue; - } - if symbol.kind() == SymbolKind::Section || symbol.address() != target_address { - if symbol.address() < target_address - && symbol.size() != 0 - && (closest_symbol.is_none() - || matches!(&closest_symbol, Some(s) if s.address() <= symbol.address())) - { - closest_symbol = Some(symbol); - } - continue; - } - return to_obj_symbol(obj_file, &symbol, 0); - } - - if let Some(symbol) = closest_symbol { - let addend = target_address - symbol.address(); - Ok(to_obj_symbol(obj_file, &symbol, addend as i64)?) - } else { - let addend = target_address - section.address(); - Ok(ObjSymbol { - name: section.name()?.to_string(), - demangled_name: None, - address: section.address(), - section_address: 0, - size: section.size(), - size_known: true, - flags: Default::default(), - addend: addend as i64, - kind: ObjSymbolKind::Unknown, - }) - } -} diff --git a/src/util/map.rs b/src/util/map.rs index 1bea5fb..511389b 100644 --- a/src/util/map.rs +++ b/src/util/map.rs @@ -51,7 +51,8 @@ struct SectionOrder { unit_order: Vec<(String, Vec)>, } -fn is_code_section(section: &str) -> bool { section == ".text" || section == ".init" } +#[inline] +fn is_code_section(section: &str) -> bool { matches!(section, ".text" | ".init") } /// Iterate over the BTreeMap and generate an ordered list of symbols and TUs by address. fn resolve_section_order( diff --git a/src/util/mod.rs b/src/util/mod.rs index 8d523de..eca3a2e 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -2,3 +2,4 @@ pub(crate) mod asm; pub(crate) mod elf; pub(crate) mod map; pub(crate) mod obj; +pub(crate) mod split; diff --git a/src/util/obj.rs b/src/util/obj.rs index 78119fd..c406052 100644 --- a/src/util/obj.rs +++ b/src/util/obj.rs @@ -1,5 +1,9 @@ -use std::{collections::BTreeMap, path::PathBuf}; +use std::{ + collections::{btree_map, BTreeMap}, + hash::{Hash, Hasher}, +}; +use anyhow::{Error, Result}; use flagset::{flags, FlagSet}; flags! { @@ -8,14 +12,20 @@ flags! { Local, Weak, Common, + Hidden, } } -#[derive(Debug, Copy, Clone, Default)] +#[derive(Debug, Copy, Clone, Default, Eq, PartialEq)] pub struct ObjSymbolFlagSet(pub(crate) FlagSet); -#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[allow(clippy::derive_hash_xor_eq)] +impl Hash for ObjSymbolFlagSet { + fn hash(&self, state: &mut H) { self.0.bits().hash(state) } +} +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ObjSectionKind { Code, Data, + ReadOnlyData, Bss, } #[derive(Debug, Clone)] @@ -25,39 +35,49 @@ pub struct ObjSection { pub address: u64, pub size: u64, pub data: Vec, + pub align: u64, pub index: usize, - pub symbols: Vec, pub relocations: Vec, + pub original_address: u64, pub file_offset: u64, } -#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default)] pub enum ObjSymbolKind { + #[default] Unknown, Function, Object, + Section, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Default)] pub struct ObjSymbol { pub name: String, pub demangled_name: Option, pub address: u64, - pub section_address: u64, + pub section: Option, pub size: u64, pub size_known: bool, pub flags: ObjSymbolFlagSet, - pub addend: i64, pub kind: ObjSymbolKind, } -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum ObjKind { + /// Fully linked file + Executable, + /// Relocatable file + Relocatable, +} +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ObjArchitecture { PowerPc, } #[derive(Debug, Clone)] pub struct ObjInfo { + pub kind: ObjKind, pub architecture: ObjArchitecture, - pub path: PathBuf, + pub name: String, + pub symbols: Vec, pub sections: Vec, - pub common: Vec, pub entry: u32, // Linker generated @@ -71,7 +91,7 @@ pub struct ObjInfo { pub splits: BTreeMap, pub link_order: Vec, } -#[derive(Debug, Eq, PartialEq, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ObjRelocKind { Absolute, PpcAddr16Hi, @@ -85,6 +105,58 @@ pub enum ObjRelocKind { pub struct ObjReloc { pub kind: ObjRelocKind, pub address: u64, - pub target: ObjSymbol, - pub target_section: Option, + pub target_symbol: usize, + pub addend: i64, +} + +impl ObjInfo { + pub fn symbols_for_section( + &self, + section_idx: usize, + ) -> impl Iterator { + self.symbols + .iter() + .enumerate() + .filter(move |&(_, symbol)| symbol.section == Some(section_idx)) + } + + pub fn build_symbol_map(&self, section_idx: usize) -> Result>> { + let mut symbols = BTreeMap::>::new(); + for (symbol_idx, symbol) in self.symbols_for_section(section_idx) { + let address = symbol.address as u32; + nested_push(&mut symbols, address, symbol_idx); + } + Ok(symbols) + } +} + +impl ObjSection { + pub fn build_relocation_map(&self) -> Result> { + let mut relocations = BTreeMap::::new(); + for reloc in &self.relocations { + let address = reloc.address as u32; + match relocations.entry(address) { + btree_map::Entry::Vacant(e) => { + e.insert(reloc.clone()); + } + btree_map::Entry::Occupied(_) => { + return Err(Error::msg(format!("Duplicate relocation @ {address:#010X}"))); + } + } + } + Ok(relocations) + } +} + +#[inline] +pub fn nested_push(map: &mut BTreeMap>, v1: T1, v2: T2) +where T1: Ord { + match map.entry(v1) { + btree_map::Entry::Occupied(mut e) => { + e.get_mut().push(v2); + } + btree_map::Entry::Vacant(e) => { + e.insert(vec![v2]); + } + } } diff --git a/src/util/split.rs b/src/util/split.rs new file mode 100644 index 0000000..1a1db0e --- /dev/null +++ b/src/util/split.rs @@ -0,0 +1,233 @@ +use std::{cmp::min, collections::HashMap}; + +use anyhow::{Error, Result}; + +use crate::util::obj::{ + ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol, +}; + +/// Split an executable object into relocatable objects. +pub fn split_obj(obj: &ObjInfo) -> Result> { + if obj.kind != ObjKind::Executable { + return Err(Error::msg(format!("Expected executable object, got {:?}", obj.kind))); + } + + let mut objects: Vec = vec![]; + let mut object_symbols: Vec>> = vec![]; + let mut name_to_obj: HashMap = HashMap::new(); + for unit in &obj.link_order { + name_to_obj.insert(unit.clone(), objects.len()); + object_symbols.push(vec![None; obj.symbols.len()]); + objects.push(ObjInfo { + kind: ObjKind::Relocatable, + architecture: ObjArchitecture::PowerPc, + name: unit.clone(), + symbols: vec![], + sections: vec![], + entry: 0, + stack_address: None, + stack_end: None, + db_stack_addr: None, + arena_lo: None, + arena_hi: None, + splits: Default::default(), + link_order: vec![], + }); + } + + for (section_idx, section) in obj.sections.iter().enumerate() { + let mut current_address = section.address as u32; + let mut section_end = (section.address + section.size) as u32; + // .ctors and .dtors end with a linker-generated null pointer, + // adjust section size appropriately + if matches!(section.name.as_str(), ".ctors" | ".dtors") + && section.data[section.data.len() - 4..] == [0u8; 4] + { + section_end -= 4; + } + let mut file_iter = obj.splits.range(current_address..).peekable(); + + // Build address to relocation / address to symbol maps + let relocations = section.build_relocation_map()?; + let symbols = obj.build_symbol_map(section_idx)?; + + loop { + if current_address >= section_end { + break; + } + + let (file_addr, unit) = match file_iter.next() { + Some((&addr, unit)) => (addr, unit), + None => return Err(Error::msg("No file found")), + }; + if file_addr > current_address { + return Err(Error::msg(format!( + "Gap in files: {} @ {:#010X}, {} @ {:#010X}", + section.name, section.address, unit, file_addr + ))); + } + let mut file_end = section_end; + if let Some(&(&next_addr, _)) = file_iter.peek() { + file_end = min(next_addr, section_end); + } + + let file = name_to_obj + .get(unit) + .and_then(|&idx| objects.get_mut(idx)) + .ok_or_else(|| Error::msg(format!("Unit '{unit}' not in link order")))?; + let symbol_idxs = name_to_obj + .get(unit) + .and_then(|&idx| object_symbols.get_mut(idx)) + .ok_or_else(|| Error::msg(format!("Unit '{unit}' not in link order")))?; + let data = match section.kind { + ObjSectionKind::Bss => vec![], + _ => section.data[(current_address as u64 - section.address) as usize + ..(file_end as u64 - section.address) as usize] + .to_vec(), + }; + + // Calculate & verify section alignment + let mut align = default_section_align(section); + if current_address & (align as u32 - 1) != 0 { + log::warn!( + "Alignment for {} {} expected {}, but starts at {:#010X}", + unit, + section.name, + align, + current_address + ); + align = 4; + } + if current_address & (align as u32 - 1) != 0 { + return Err(Error::msg(format!( + "Invalid alignment for split: {} {} {:#010X}", + unit, section.name, current_address + ))); + } + + // Collect relocations; target_symbol will be updated later + let out_relocations = relocations + .range(current_address..file_end) + .map(|(_, o)| ObjReloc { + kind: o.kind, + address: o.address - current_address as u64, + target_symbol: o.target_symbol, + addend: o.addend, + }) + .collect(); + + let out_section_idx = file.sections.len(); + file.sections.push(ObjSection { + name: section.name.clone(), + kind: section.kind, + address: 0, + size: file_end as u64 - current_address as u64, + data, + align, + index: out_section_idx, + relocations: out_relocations, + original_address: current_address as u64, + file_offset: section.file_offset + (current_address as u64 - section.address), + }); + + // Add section symbols + for &symbol_idx in symbols.range(current_address..file_end).flat_map(|(_, vec)| vec) { + if symbol_idxs[symbol_idx].is_some() { + continue; // should never happen? + } + let symbol = &obj.symbols[symbol_idx]; + symbol_idxs[symbol_idx] = Some(file.symbols.len()); + file.symbols.push(ObjSymbol { + name: symbol.name.clone(), + demangled_name: symbol.demangled_name.clone(), + address: symbol.address - current_address as u64, + section: Some(out_section_idx), + size: symbol.size, + size_known: symbol.size_known, + flags: symbol.flags, + kind: symbol.kind, + }); + } + + current_address = file_end; + } + } + + // Update relocations + for (obj_idx, out_obj) in objects.iter_mut().enumerate() { + let symbol_idxs = &mut object_symbols[obj_idx]; + for section in &mut out_obj.sections { + for reloc in &mut section.relocations { + match symbol_idxs[reloc.target_symbol] { + Some(out_sym_idx) => { + reloc.target_symbol = out_sym_idx; + } + None => { + // Extern + let out_sym_idx = out_obj.symbols.len(); + let target_sym = &obj.symbols[reloc.target_symbol]; + symbol_idxs[reloc.target_symbol] = Some(out_sym_idx); + out_obj.symbols.push(ObjSymbol { + name: target_sym.name.clone(), + demangled_name: target_sym.demangled_name.clone(), + ..Default::default() + }); + reloc.target_symbol = out_sym_idx; + } + } + } + } + } + + // Strip linker generated symbols + for obj in &mut objects { + for symbol in &mut obj.symbols { + if is_skip_symbol(&symbol.name) { + if symbol.section.is_some() { + log::debug!("Externing {:?} in {}", symbol, obj.name); + *symbol = ObjSymbol { + name: symbol.name.clone(), + demangled_name: symbol.demangled_name.clone(), + ..Default::default() + }; + } + } else if is_linker_symbol(&symbol.name) { + if let Some(section_idx) = symbol.section { + log::debug!("Skipping {:?} in {}", symbol, obj.name); + let section = &mut obj.sections[section_idx]; + // TODO assuming end of file + section.size -= symbol.size; + section.data.truncate(section.data.len() - symbol.size as usize); + *symbol = ObjSymbol { + name: symbol.name.clone(), + demangled_name: symbol.demangled_name.clone(), + ..Default::default() + }; + } + } + } + } + + Ok(objects) +} + +/// mwld doesn't preserve the original section alignment values +fn default_section_align(section: &ObjSection) -> u64 { + match section.kind { + ObjSectionKind::Code => 4, + _ => match section.name.as_str() { + ".ctors" | ".dtors" | "extab" | "extabindex" => 4, + _ => 8, + }, + } +} + +/// Linker-generated symbols to extern +#[inline] +fn is_skip_symbol(name: &str) -> bool { matches!(name, "_ctors" | "_dtors") } + +/// Linker generated symbols to strip entirely +#[inline] +fn is_linker_symbol(name: &str) -> bool { + matches!(name, "_eti_init_info" | "_rom_copy_info" | "_bss_init_info") +}