Add `elf split`; rework asm generation

`elf split` allows splitting an ELF
directly into relocatable object files
This commit is contained in:
Luke Street 2022-12-24 03:10:12 -05:00
parent 54f2abd35f
commit 9f4cc2f542
8 changed files with 909 additions and 625 deletions

View File

@ -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());
}
}

View File

@ -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::<String, object::write::Object>::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<SectionId>],
) -> Result<object::write::SymbolSection> {
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<SectionIndex>) -> Result<SymbolFlags<SectionId>> {
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(

View File

@ -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<P: AsRef<Path>>(path: P, obj: &ObjInfo) -> Result<()> {
let mut file_map = HashMap::<String, BufWriter<File>>::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: Write>(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::<ObjSymbol>::new();
let mut addr_sym = BTreeMap::<u32, Vec<SymbolEntry>>::new();
for section in &obj.sections {
for symbol in &section.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<ObjSymbol> = obj.symbols.clone();
let mut section_entries: Vec<BTreeMap<u32, Vec<SymbolEntry>>> = vec![];
let mut section_relocations: Vec<BTreeMap<u32, ObjReloc>> = vec![];
for (section_idx, section) in obj.sections.iter().enumerate() {
// Build symbol start/end entries
let mut entries = BTreeMap::<u32, Vec<SymbolEntry>>::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(&section.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 &section.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<P: AsRef<Path>>(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(&section.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 = &section_entries[section.index];
let relocations = &section_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::<u32, ObjReloc>::new();
for reloc in &section.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 == &current_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,
&section_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(&section_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<File>,
fn write_code_chunk<W: Write>(
w: &mut W,
symbols: &[ObjSymbol],
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
_entries: &BTreeMap<u32, Vec<SymbolEntry>>,
relocations: &BTreeMap<u32, ObjReloc>,
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<ObjReloc> = 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<File>,
fn write_ins<W: Write>(
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<File>, reloc: &ObjReloc) -> Result<()> {
write_symbol(w, &reloc.target)?;
fn write_reloc<W: Write>(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<File>, reloc: &ObjReloc) -> Result<()> {
Ok(())
}
fn write_symbol_entry(
w: &mut BufWriter<File>,
fn write_symbol_entry<W: Write>(
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<File>,
fn write_data<W: Write>(
w: &mut W,
symbols: &[ObjSymbol],
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
entries: &BTreeMap<u32, Vec<SymbolEntry>>,
relocations: &BTreeMap<u32, ObjReloc>,
section: &ObjSection,
start: u32,
end: u32,
section_entries: &[BTreeMap<u32, Vec<SymbolEntry>>],
) -> 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<File>, data: &[u8]) -> Result<()> {
fn write_data_chunk<W: Write>(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<File>, data: &[u8]) -> Result<()> {
Ok(())
}
fn write_data_reloc(
w: &mut BufWriter<File>,
fn write_data_reloc<W: Write>(
w: &mut W,
symbols: &[ObjSymbol],
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
_entries: &BTreeMap<u32, Vec<SymbolEntry>>,
reloc: &ObjReloc,
section_entries: &[BTreeMap<u32, Vec<SymbolEntry>>],
) -> Result<u32> {
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<File>,
fn write_bss<W: Write>(
w: &mut W,
symbols: &[ObjSymbol],
sym_map: &BTreeMap<u32, Vec<SymbolEntry>>,
entries: &BTreeMap<u32, Vec<SymbolEntry>>,
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<String, BufWriter<File>>,
unit: &String,
fn write_section_header<W: Write>(
w: &mut W,
section: &ObjSection,
subsection: usize,
start: u32,
end: u32,
) -> Result<&'a mut BufWriter<File>> {
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<File>, 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: Write>(
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<File>, name: &str) -> std::io::Result<()> {
fn write_symbol_name<W: Write>(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<File>, 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) */)

View File

@ -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<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
@ -31,13 +33,15 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
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<u32> = None;
let mut stack_end: Option<u32> = None;
@ -45,7 +49,36 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let mut arena_lo: Option<u32> = None;
let mut arena_hi: Option<u32> = None;
let mut common: Vec<ObjSymbol> = vec![];
let mut sections: Vec<ObjSection> = vec![];
let mut section_indexes: Vec<Option<usize>> = 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<ObjSymbol> = vec![];
let mut symbol_indexes: Vec<Option<usize>> = vec![];
let mut current_file: Option<String> = None;
let mut section_starts = IndexMap::<String, Vec<(u64, String)>>::new();
for symbol in obj_file.symbols() {
@ -54,23 +87,18 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
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<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
// 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<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
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) = &current_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<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
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) = &current_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<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
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, &section_indexes)?);
}
// Link order is trivially deduced
let mut link_order = Vec::<String>::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::<u32, String>::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<ObjSection> = 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<ObjSymbol> = 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<ObjReloc> = vec![];
for (address, reloc) in section.relocations() {
relocations.push(to_obj_reloc(&obj_file, &section, &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<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
})
}
pub fn write_elf(obj: &ObjInfo) -> Result<object::write::Object> {
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<SectionId> = 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<SymbolId> = 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 &section.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<usize>],
) -> Result<ObjSymbol> {
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) = &section {
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<usize>],
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<ObjSymbol> {
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<Symbol<'_, '_>> = 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,
})
}
}

View File

@ -51,7 +51,8 @@ struct SectionOrder {
unit_order: Vec<(String, Vec<String>)>,
}
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(

View File

@ -2,3 +2,4 @@ pub(crate) mod asm;
pub(crate) mod elf;
pub(crate) mod map;
pub(crate) mod obj;
pub(crate) mod split;

View File

@ -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<ObjSymbolFlags>);
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for ObjSymbolFlagSet {
fn hash<H: Hasher>(&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<u8>,
pub align: u64,
pub index: usize,
pub symbols: Vec<ObjSymbol>,
pub relocations: Vec<ObjReloc>,
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<String>,
pub address: u64,
pub section_address: u64,
pub section: Option<usize>,
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<ObjSymbol>,
pub sections: Vec<ObjSection>,
pub common: Vec<ObjSymbol>,
pub entry: u32,
// Linker generated
@ -71,7 +91,7 @@ pub struct ObjInfo {
pub splits: BTreeMap<u32, String>,
pub link_order: Vec<String>,
}
#[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<String>,
pub target_symbol: usize,
pub addend: i64,
}
impl ObjInfo {
pub fn symbols_for_section(
&self,
section_idx: usize,
) -> impl Iterator<Item = (usize, &ObjSymbol)> {
self.symbols
.iter()
.enumerate()
.filter(move |&(_, symbol)| symbol.section == Some(section_idx))
}
pub fn build_symbol_map(&self, section_idx: usize) -> Result<BTreeMap<u32, Vec<usize>>> {
let mut symbols = BTreeMap::<u32, Vec<usize>>::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<BTreeMap<u32, ObjReloc>> {
let mut relocations = BTreeMap::<u32, ObjReloc>::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<T1, T2>(map: &mut BTreeMap<T1, Vec<T2>>, 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]);
}
}
}

233
src/util/split.rs Normal file
View File

@ -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<Vec<ObjInfo>> {
if obj.kind != ObjKind::Executable {
return Err(Error::msg(format!("Expected executable object, got {:?}", obj.kind)));
}
let mut objects: Vec<ObjInfo> = vec![];
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
let mut name_to_obj: HashMap<String, usize> = 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")
}