Add `elf split`; rework asm generation
`elf split` allows splitting an ELF directly into relocatable object files
This commit is contained in:
parent
54f2abd35f
commit
9f4cc2f542
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
216
src/cmd/elf.rs
216
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::<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(
|
||||
|
|
594
src/util/asm.rs
594
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<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 §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<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(§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<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(§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::<u32, ObjReloc>::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<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) */)
|
||||
|
|
381
src/util/elf.rs
381
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<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) = ¤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<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) = ¤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<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, §ion_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, §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<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 §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<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) = §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<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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -2,3 +2,4 @@ pub(crate) mod asm;
|
|||
pub(crate) mod elf;
|
||||
pub(crate) mod map;
|
||||
pub(crate) mod obj;
|
||||
pub(crate) mod split;
|
||||
|
|
100
src/util/obj.rs
100
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<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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
Loading…
Reference in New Issue